<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>HanSol's Oak Cask</title>
    <link>https://hansol0607.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Sun, 5 Apr 2026 06:46:07 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>HanSol_Lim</managingEditor>
    <image>
      <title>HanSol's Oak Cask</title>
      <url>https://tistory1.daumcdn.net/tistory/6854886/attach/09208687e9fa482c9fddf80bc7d62956</url>
      <link>https://hansol0607.tistory.com</link>
    </image>
    <item>
      <title>JUnit4의 @Test(expected = ...)는 JUnit5에서 지원 X</title>
      <link>https://hansol0607.tistory.com/83</link>
      <description>&lt;h1&gt;✅ JUnit4 @Test(expected = ...) vs JUnit5 assertThrows() 차이 정리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JUnit5로 넘어오면서 기존 JUnit4에서 사용하던 예외 테스트 방식이 달라졌습니다. 아래에서 차이점과 적용 방법을 정리해보았습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. JUnit4 방식 (✅ 가능)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JUnit4에서는 @Test 어노테이션에 expected 속성을 이용하여 예외 발생 여부를 테스트했습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;@Test(expected = IllegalArgumentException.class)
public void testExceptionThrown() {
    someMethodThatThrows();  // 예외가 발생해야 테스트 통과
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. JUnit5 방식 (❌ expected 불가, ✅ assertThrows 사용)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JUnit5에서는 @Test(expected = ...) 문법이 제거되었습니다. 대신, Assertions.assertThrows() 메서드를 사용해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;import static org.junit.jupiter.api.Assertions.assertThrows;

@Test
void testExceptionThrown() {
    assertThrows(IllegalArgumentException.class, () -&amp;gt; {
        someMethodThatThrows();
    });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  예외 메시지까지 검증하고 싶다면?&lt;/h3&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@Test
void testExceptionWithMessage() {
    IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -&amp;gt; {
        someMethodThatThrows();
    });
    assertEquals(&quot;예상 에러 메시지&quot;, exception.getMessage());
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✅ 정리 표&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;항목&amp;nbsp;&lt;/td&gt;
&lt;td&gt;JUnit4&lt;/td&gt;
&lt;td&gt;JUnit5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;예외 테스트 방식&lt;/td&gt;
&lt;td&gt;@Test(expected = ...)&lt;/td&gt;
&lt;td&gt;assertThrows() 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;예외 메시지 검증&lt;/td&gt;
&lt;td&gt;불편함&lt;/td&gt;
&lt;td&gt;가능 (getMessage())&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;람다식 사용&lt;/td&gt;
&lt;td&gt;불필요&lt;/td&gt;
&lt;td&gt;필수 (() -&amp;gt; {})&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✍️ 결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JUnit5에서는 @Test(expected = ...) 문법이 &lt;b&gt;삭제되었으므로&lt;/b&gt;,&lt;br /&gt;&lt;b&gt;반드시 assertThrows()&lt;/b&gt; 로 예외 발생 여부를 확인해야 합니다.&lt;br /&gt;더 정교하게 예외 메시지나 속성을 검증할 수도 있어 &lt;b&gt;유연성&lt;/b&gt;이 높아졌습니다.&lt;/p&gt;</description>
      <category>GPT가 말아주는 지식</category>
      <category>Expected</category>
      <category>JUnit</category>
      <category>JUnit5</category>
      <category>테스트</category>
      <author>HanSol_Lim</author>
      <guid isPermaLink="true">https://hansol0607.tistory.com/83</guid>
      <comments>https://hansol0607.tistory.com/83#entry83comment</comments>
      <pubDate>Wed, 26 Mar 2025 10:31:12 +0900</pubDate>
    </item>
    <item>
      <title>Builder 패턴</title>
      <link>https://hansol0607.tistory.com/82</link>
      <description>&lt;h1&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;b&gt;1. Builder 패턴이란?&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Builder 패턴은 &lt;b&gt;객체 생성 과정이 복잡할 때, 단계적으로 객체를 생성할 수 있도록 도와주는 디자인 패턴&lt;/b&gt;입니다.&lt;br /&gt;즉, &lt;b&gt;객체의 생성 과정과 표현 방식을 분리&lt;/b&gt;하여, &lt;b&gt;가독성과 유지보수성을 높이는 목적&lt;/b&gt;으로 사용됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. 왜 Builder 패턴을 사용할까?&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ &lt;b&gt;1) 생성자(Constructors) 매개변수 난잡함 문제 해결&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생성자에 많은 매개변수가 필요하면 &lt;b&gt;매개변수 순서 실수&lt;/b&gt;가 발생할 가능성이 높음.&lt;/li&gt;
&lt;li&gt;일부 값만 설정하고 싶은 경우 &lt;b&gt;불필요한 null 값을 넣어야 하는 문제&lt;/b&gt;가 발생.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;문제 코드 (생성자 사용)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;class Car {
    private String brand;
    private String model;
    private int year;
    private boolean sunroof;
    private boolean navigation;

    public Car(String brand, String model, int year, boolean sunroof, boolean navigation) {
        this.brand = brand;
        this.model = model;
        this.year = year;
        this.sunroof = sunroof;
        this.navigation = navigation;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;  문제점:&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Car 객체를 생성하려면 모든 인자를 기억해서 넣어야 함.&lt;/li&gt;
&lt;li&gt;일부 옵션만 넣고 싶어도 null이나 기본값을 수동으로 지정해야 함.&lt;/li&gt;
&lt;li&gt;매개변수 순서를 실수하면 &lt;b&gt;컴파일 오류는 발생하지 않지만, 잘못된 값이 들어가는 위험&lt;/b&gt;이 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ &lt;b&gt;2) 생성자 오버로딩(Overloading) 문제 해결&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다양한 조합의 객체를 만들기 위해 &lt;b&gt;생성자 오버로딩을 많이 사용하면 코드가 길어지고 유지보수가 어려움&lt;/b&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;문제 코드 (생성자 오버로딩)&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;class Car {
    private String brand;
    private String model;
    private int year;
    private boolean sunroof;
    private boolean navigation;

    public Car(String brand, String model) {
        this(brand, model, 2023, false, false);
    }

    public Car(String brand, String model, int year) {
        this(brand, model, year, false, false);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;  문제점:&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;생성자 조합이 많아지면 코드가 길어지고 유지보수가 어려움.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;새로운 필드가 추가될 경우 &lt;b&gt;모든 생성자를 수정해야 하는 불편함 발생&lt;/b&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. Builder 패턴 적용 코드&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  Builder 패턴을 적용하면, 복잡한 객체 생성을 유연하고 직관적으로 만들 수 있음.&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ &lt;b&gt;(1) Builder 패턴 적용 코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;class Car {
    private String brand;
    private String model;
    private int year;
    private boolean sunroof;
    private boolean navigation;

    public static class Builder {
        private String brand;
        private String model;
        private int year = 2023;
        private boolean sunroof = false;
        private boolean navigation = false;

        public Builder(String brand, String model) {
            this.brand = brand;
            this.model = model;
        }

        public Builder year(int year) {
            this.year = year;
            return this;
        }

        public Builder sunroof(boolean sunroof) {
            this.sunroof = sunroof;
            return this;
        }

        public Builder navigation(boolean navigation) {
            this.navigation = navigation;
            return this;
        }

        public Car build() {
            return new Car(this);
        }
    }

    private Car(Builder builder) {
        this.brand = builder.brand;
        this.model = builder.model;
        this.year = builder.year;
        this.sunroof = builder.sunroof;
        this.navigation = builder.navigation;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ &lt;b&gt;(2) Lombok을 활용한 간결한 Builder 패턴&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;import lombok.Builder;
import lombok.ToString;

@Builder // Lombok이 자동으로 Builder 패턴 적용
@ToString
class Car {
    private String brand;
    private String model;
    private int year;
    private boolean sunroof;
    private boolean navigation;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. Builder 패턴의 단점&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;1) 코드의 복잡성이 증가&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Builder 패턴을 적용하면 &lt;b&gt;기본적인 생성자보다 코드량이 증가&lt;/b&gt;할 수 있음.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;대안:&lt;/b&gt; Lombok을 사용하면 코드 길이를 줄일 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;2) 객체 생성 비용이 증가&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Builder 패턴은 &lt;b&gt;매번 새로운 객체를 생성&lt;/b&gt;해야 하므로, 성능이 중요한 애플리케이션에서는 불리할 수 있음.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;대안:&lt;/b&gt; 객체 풀링(Object Pooling) 기법을 활용할 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;3) 필수 값 검증이 어려움&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생성자는 필수 매개변수를 강제할 수 있지만, Builder 패턴은 &lt;b&gt;필수 필드 없이도 객체가 생성될 위험이 있음&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;대안:&lt;/b&gt; Builder 내부에서 필수 필드를 검증하는 로직 추가.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;@Builder
class Car {
    private String brand;
    private String model;

    public static class CarBuilder {
        public Car build() {
            if (brand == null || model == null) {
                throw new IllegalStateException(&quot;Brand and model are required fields!&quot;);
            }
            return new Car(brand, model);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;5. 결론&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;Builder 패턴을 활용하면 복잡한 객체 생성을 쉽게 관리할 수 있지만, 코드 복잡성 증가와 객체 생성 비용 증가 등의 단점도 고려해야 한다.&lt;/b&gt;&lt;br /&gt;✅ Lombok을 활용하면 Builder 패턴을 더욱 간결하게 적용할 수 있음.&lt;br /&gt;  &lt;b&gt;즉, 객체 생성이 복잡해질수록 Builder 패턴을 활용하면 코드가 더 명확해진다!&lt;/b&gt;&lt;/p&gt;</description>
      <category>디자인 패턴/생성 패턴</category>
      <category>디자인패턴</category>
      <category>빌더</category>
      <category>생성패턴</category>
      <author>HanSol_Lim</author>
      <guid isPermaLink="true">https://hansol0607.tistory.com/82</guid>
      <comments>https://hansol0607.tistory.com/82#entry82comment</comments>
      <pubDate>Wed, 5 Mar 2025 16:01:27 +0900</pubDate>
    </item>
    <item>
      <title>전자정부 프레임워크(e-Government Framework)</title>
      <link>https://hansol0607.tistory.com/80</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;전자정부 프레임워크(e-Government Framework)란?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;정의&lt;/b&gt;&lt;br /&gt;정부가 공공부문의 정보 시스템을 개발하고 운영하는 데 있어 &quot;&lt;b&gt;표준화된 개발 환경&quot;&lt;/b&gt;을 제공하기 위해 만들어진 &lt;b&gt;Java 기반의 오픈 소스 웹 애플리케이션 프레임워크&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  전자정부 프레임워크의 등장 배경&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거 공공기관들은 개별적으로 IT 시스템을 개발 &amp;rarr; &lt;b&gt;일관되지 않은 개발 환경, 코드 스타일 차이 발생&lt;/b&gt;&lt;br /&gt;➡ &lt;b&gt;통합 및 유지보수 어려움, 시스템 간 호환성 및 연계성 부족&lt;/b&gt;&lt;br /&gt;➡ &lt;b&gt;높은 유지보수 비용, 낮은 생산성, 보안 문제, 신뢰성 부족&lt;/b&gt;&lt;br /&gt;➡ &lt;b&gt;이 문제를 해결하기 위해 개발 표준화 필요!&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;✅ 전자정부 프레임워크의 장단점&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  장점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ &lt;b&gt;표준화된 개발 환경 제공&lt;/b&gt; &amp;rarr; 개발 일관성 유지&lt;br /&gt;✔ &lt;b&gt;재사용성 및 확장성 확보&lt;/b&gt; &amp;rarr; 공통 모듈 활용 가능&lt;br /&gt;✔ &lt;b&gt;오픈 소스 기반&lt;/b&gt; &amp;rarr; 라이선스 비용 절감&lt;br /&gt;✔ &lt;b&gt;상호 운용성 확보&lt;/b&gt; &amp;rarr; 기관 간 시스템 연계 용이&lt;br /&gt;✔ &lt;b&gt;보안성 강화&lt;/b&gt; &amp;rarr; Spring Security 기반 보안 제공&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  단점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;⚠ &lt;b&gt;최신 기술 반영 속도가 느림&lt;/b&gt;&lt;br /&gt;⚠ &lt;b&gt;공공 프로젝트에만 최적화됨&lt;/b&gt; (민간 기업에서는 필요성 낮음)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  전자정부 프레임워크는 어디에서 사용될까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✔ &lt;b&gt;정부기관 및 공공기관&lt;/b&gt;&lt;br /&gt;✔ &lt;b&gt;공공 SI(System Integration) 프로젝트&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;왜 이걸 써야 할까?&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정부기관이 발주하는 사업을 수주하기 위해 필수적&lt;/li&gt;
&lt;li&gt;&lt;b&gt;RFP(제안요청서)에 전자정부 프레임워크 적용 요구사항 포함&lt;/b&gt;&lt;br /&gt;➡ &lt;b&gt;공공사업을 수행하는 SI 업체라면 반드시 사용해야 함&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  전자정부 프레임워크의 구성 요소&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1️⃣ 개발 환경&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;IDE&lt;/b&gt;: Eclipse 기반의 eGovFrame 전용 플러그인 제공&lt;/li&gt;
&lt;li&gt;&lt;b&gt;빌드 도구&lt;/b&gt;: Maven, Gradle 지원&lt;/li&gt;
&lt;li&gt;&lt;b&gt;형상 관리&lt;/b&gt;: Git, SVN 등과 통합 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2️⃣ 프레임워크 핵심 구조&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;MVC 패턴&lt;/b&gt;: Spring Framework 기반의 아키텍처 제공&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Spring Framework&lt;/b&gt;: IoC/DI 기반 객체 관리 및 AOP 지원&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MyBatis, JPA&lt;/b&gt;: 데이터베이스 연동 ORM 및 SQL 매핑 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3️⃣ 공통 컴포넌트&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;인증/권한 관리&lt;/b&gt;: Spring Security 기반 보안 기능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;배치 처리&lt;/b&gt;: 대용량 데이터 처리를 위한 Spring Batch 지원&lt;/li&gt;
&lt;li&gt;&lt;b&gt;메시징&lt;/b&gt;: 시스템 간 비동기 통신 기능 제공&lt;/li&gt;
&lt;li&gt;&lt;b&gt;파일 업로드/다운로드, 로그 관리, 코드 관리 등 다양한 공통 모듈 포함&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4️⃣ 운영 환경&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;서버&lt;/b&gt;: Tomcat, Jeus, WebLogic 등 다양한 WAS(Web Application Server) 지원&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DBMS&lt;/b&gt;: Oracle, MySQL, PostgreSQL 등과의 호환성 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  전자정부 프레임워크 시작하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;a href=&quot;https://www.egovframe.go.kr/home/main.do&quot;&gt;공식 홈페이지&lt;/a&gt;&lt;/p&gt;</description>
      <category>용어, 개념</category>
      <category>전자정부프레임워크</category>
      <author>HanSol_Lim</author>
      <guid isPermaLink="true">https://hansol0607.tistory.com/80</guid>
      <comments>https://hansol0607.tistory.com/80#entry80comment</comments>
      <pubDate>Thu, 27 Feb 2025 10:59:41 +0900</pubDate>
    </item>
    <item>
      <title>LRS (Learning Record Store)란 무엇인가?</title>
      <link>https://hansol0607.tistory.com/79</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;1. LRS의 정의&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;**LRS (Learning Record Store)**는 학습 경험 데이터를 &lt;b&gt;수집&lt;/b&gt;, &lt;b&gt;저장&lt;/b&gt;, &lt;b&gt;관리&lt;/b&gt;, &lt;b&gt;분석&lt;/b&gt;할 수 있는 &lt;b&gt;시스템&lt;/b&gt;입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;a href=&quot;https://hansol0607.tistory.com/78&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;xAPI&lt;/a&gt; (Experience API, Tin Can API)&lt;/b&gt; 표준을 따르는 데이터의 저장소 역할을 합니다.&lt;/li&gt;
&lt;li&gt;학습자의 온라인/오프라인 활동, 모바일 학습, 게임 기반 학습, 현장 교육 등 다양한 &lt;b&gt;학습 경험 데이터&lt;/b&gt;를 중앙 집중식으로 수집하고 보관합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ &lt;b&gt;2. LRS의 실체: 서버인가, 데이터베이스인가?&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  &lt;b&gt;LRS는 단순한 DB가 아닌 '시스템'이다.&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LRS는 &lt;b&gt;서버 + 데이터베이스 + API 인터페이스&lt;/b&gt;를 포함하는 &lt;b&gt;웹 기반 애플리케이션 시스템&lt;/b&gt;입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt; 구성 요소 &lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt; &lt;span style=&quot;text-align: start;&quot;&gt;역할&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt; 기술 예시 &lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;서버 (Application Layer)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;xAPI 데이터 수신, 처리, 인증, API 제공&lt;/td&gt;
&lt;td&gt;Node.js, Java(Spring), Python(Django)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;데이터베이스 (Storage Layer)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;학습 데이터 영구 저장 및 검색&lt;/td&gt;
&lt;td&gt;MongoDB, PostgreSQL, MySQL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;API 인터페이스&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;RESTful API 제공, 외부 시스템과 데이터 연동&lt;/td&gt;
&lt;td&gt;JSON over HTTPS, OAuth 2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;결론&lt;/b&gt;: LRS는&amp;nbsp;&lt;b&gt;애플리케이션 시스템&lt;/b&gt;입니다. 서버는 xAPI 데이터를 수신 및 처리하고, 데이터베이스는 이를 저장하고 검색하는 역할을 수행합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;3. LRS의 핵심 기능&lt;/b&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;학습 경험 데이터 저장&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;xAPI 표준에 따라 수집된 JSON 형태의 데이터를 저장&lt;/li&gt;
&lt;li&gt;학습자의 모든 활동 데이터를 시간 순서대로 기록&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터 검색 및 제공&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;REST API를 통해 특정 학습자나 활동에 대한 데이터를 외부 시스템에서 조회 가능&lt;/li&gt;
&lt;li&gt;실시간 데이터 제공으로 분석 및 피드백 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;시스템 간 상호 운용성&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LMS, HR 시스템, 게임, 모바일 앱, IoT 기기 등 다양한 시스템과 통합&lt;/li&gt;
&lt;li&gt;플랫폼 간 학습 데이터 일관성 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;데이터 분석 및 시각화&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;학습 데이터에 대한 통계 분석 및 보고서 생성&lt;/li&gt;
&lt;li&gt;대시보드를 통한 실시간 데이터 모니터링 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;보안 및 인증&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OAuth 2.0, Basic Auth 등을 통한 안전한 데이터 접근 제어&lt;/li&gt;
&lt;li&gt;HTTPS를 통한 데이터 전송 보안&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;4. LRS의 데이터 흐름 및 동작 방식&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;[Client (웹/모바일/앱/IoT)]
        │
    (xAPI 진술문 전송 - HTTP POST 요청)
        │
[API 서버 (LRS 엔진 - Node.js, Python 등)]
        │
    (진술문 유효성 검사, 데이터 정규화)
        │
[Database (MongoDB, PostgreSQL 등)]
        │
    (데이터 저장 및 검색 제공)
        │
[대시보드/외부 시스템] &amp;larr; (REST API로 데이터 조회)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;✅ &lt;b&gt;예시: xAPI 진술문 데이터 전송&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;actor&quot;: {&quot;name&quot;: &quot;홍길동&quot;, &quot;mbox&quot;: &quot;mailto:honggildong@example.com&quot;},
  &quot;verb&quot;: {&quot;id&quot;: &quot;http://adlnet.gov/expapi/verbs/completed&quot;, &quot;display&quot;: {&quot;ko&quot;: &quot;완료했다&quot;}},
  &quot;object&quot;: {&quot;id&quot;: &quot;http://example.com/courses/python-basic&quot;},
  &quot;timestamp&quot;: &quot;2025-02-26T12:00:00Z&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 데이터는 LRS 서버로 전송되며, 서버는 이를 데이터베이스에 저장하고 필요할 때 외부 시스템에서 API 호출을 통해 데이터를 조회할 수 있도록 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;5. LRS와 LMS의 차이점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt; 항목 &lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt; LMS (Learning Management System) &lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt; LRS (Learning Record Store) &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;주요 기능&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;강의 관리, 학습자 등록, 시험 및 평가&lt;/td&gt;
&lt;td&gt;학습 데이터 수집, 저장, 분석, 제공&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;데이터 추적 범위&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;과정 내 학습 활동 (제한적)&lt;/td&gt;
&lt;td&gt;온라인/오프라인 모든 학습 경험 (유연함)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;표준 지원&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;주로 SCORM 사용&lt;/td&gt;
&lt;td&gt;xAPI 표준 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;인터넷 연결 여부&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;항상 연결 필요&lt;/td&gt;
&lt;td&gt;오프라인 학습 후 데이터 동기화 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;시스템 확장성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;제한적&lt;/td&gt;
&lt;td&gt;높은 확장성 및 다양한 시스템과 통합 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;6. 주요 오픈 소스 및 상용 LRS 솔루션&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt; LRS 솔루션 &lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt; 설명 &lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt; 기술 스택 &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Learning Locker&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;가장 널리 사용되는 오픈 소스 LRS, 대시보드 제공&lt;/td&gt;
&lt;td&gt;Node.js + MongoDB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;ADL LRS&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;xAPI 표준 개발 기관인 ADL에서 제공하는 LRS&lt;/td&gt;
&lt;td&gt;Java + PostgreSQL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Rustici LRS&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;SCORM 및 xAPI 표준 제공 업체의 상용 LRS 솔루션&lt;/td&gt;
&lt;td&gt;다양한 언어 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Watershed LRS&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;기업용 분석 중심 LRS, 고급 분석 및 시각화 제공&lt;/td&gt;
&lt;td&gt;AWS 기반 클라우드&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;7. LRS의 운영 환경: 리눅스 기반 여부&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;LRS는 일반적으로 리눅스 기반에서 실행됩니다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Ubuntu&lt;/b&gt;와 같은 &lt;b&gt;리눅스 환경&lt;/b&gt;에서 배포하는 것이 표준입니다.&lt;/li&gt;
&lt;li&gt;이유:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Node.js, Python, Java와 같은 백엔드 기술이 리눅스에서 최적의 성능 제공&lt;/li&gt;
&lt;li&gt;MongoDB, PostgreSQL 등 주요 데이터베이스가 리눅스에서 뛰어난 안정성 보장&lt;/li&gt;
&lt;li&gt;클라우드 환경(AWS, Azure, GCP)과의 호환성 용이&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;8. LRS의 실제 사용 사례&lt;/b&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;기업 교육 시스템&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;직원의 e-Learning 과정, 현장 실습, 웨비나 참여 데이터 추적&lt;/li&gt;
&lt;li&gt;학습 성과 분석 후 맞춤형 교육 경로 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;교육 기관&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;학생들의 학습 활동을 시간 순으로 기록하고 성과 분석&lt;/li&gt;
&lt;li&gt;개인별 피드백 및 학습 개선 전략 수립&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;헬스케어 및 의료 교육&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;의료 시뮬레이션 학습 추적 및 성능 평가&lt;/li&gt;
&lt;li&gt;의료인력의 자격 취득 과정 분석&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;게임 기반 학습 플랫폼&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;학습 게임 내의 활동과 성과를 xAPI로 기록&lt;/li&gt;
&lt;li&gt;실시간 피드백과 성과 기반 학습 전략 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;9. LRS의 장점 요약&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 120px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;   특징 &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;   설명 &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;유연한 데이터 추적&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;온라인, 오프라인, IoT 기반 학습 데이터 추적 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;시스템 독립성&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;다양한 시스템 및 디바이스와의 통합 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;실시간 데이터 처리&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;학습 활동 발생 즉시 데이터 저장 및 제공&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;확장성&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;다양한 산업과 요구 사항에 맞게 확장 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;보안성&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;OAuth 2.0, HTTPS 등 강력한 보안 기능 제공&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✨ &lt;b&gt;10. 결론: LRS의 본질&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt; &lt;b&gt;LRS&lt;/b&gt;는 xAPI&lt;/b&gt; 표준을 따르는 &lt;b&gt;학습 경험 데이터를 수집, 처리, 저장, 분석&lt;/b&gt;하는 &lt;b&gt;서버 기반 애플리케이션 시스템&lt;/b&gt;입니다.&lt;/li&gt;
&lt;li&gt;**서버(Application Layer)**가 클라이언트로부터 학습 데이터를 수신하고 처리하는 동안, **데이터베이스(Storage Layer)**는 해당 데이터를 영구적으로 저장하여 &lt;b&gt;검색, 분석, 보고&lt;/b&gt;가 가능하도록 지원합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;리눅스(Ubuntu) 기반 환경&lt;/b&gt;에서 운영되는 것이 일반적이며, 이는 &lt;b&gt;안정성&lt;/b&gt;, &lt;b&gt;보안성&lt;/b&gt;, &lt;b&gt;확장성&lt;/b&gt; 측면에서 이상적입니다.&lt;/li&gt;
&lt;li&gt;LRS는 현대적인 &lt;b&gt;데이터 중심 학습 생태계&lt;/b&gt;에서 핵심적인 역할을 수행하며, &lt;b&gt;개인화된 학습 경험&lt;/b&gt;과 &lt;b&gt;성과 중심 학습 전략&lt;/b&gt;을 지원합니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>용어, 개념</category>
      <category>LRS</category>
      <category>xapi</category>
      <author>HanSol_Lim</author>
      <guid isPermaLink="true">https://hansol0607.tistory.com/79</guid>
      <comments>https://hansol0607.tistory.com/79#entry79comment</comments>
      <pubDate>Wed, 26 Feb 2025 14:23:54 +0900</pubDate>
    </item>
    <item>
      <title>xAPI (Experience API, Tin Can API) 상세 정리</title>
      <link>https://hansol0607.tistory.com/78</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ &lt;b&gt;1. xAPI란 무엇인가?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;**xAPI (Experience API)**는 학습 활동과 경험을 추적하고 기록하는&lt;b&gt; 표준 프로토콜&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(프로토콜: &lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;복수의 컴퓨터 사이나 중앙 컴퓨터와 단말기 사이에서 데이터 통신을 원활하게 하기 위해 필요한 &lt;b&gt;통신 규약&lt;/b&gt;.)&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;이전 명칭&lt;/b&gt;: &lt;b&gt;Tin Can API, Experience API&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;개발 목적&lt;/b&gt;: 기존의 SCORM 표준이 가진 한계를 극복하여 다양한 학습 경험(온라인, 오프라인, 모바일, 시뮬레이션 등)을 추적할 수 있도록 설계&lt;/li&gt;
&lt;li&gt;&lt;b&gt;핵심 기능&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;진술문(Statement) 형식&lt;/b&gt;의 학습 경험 기록&lt;/li&gt;
&lt;li&gt;학습 경험 데이터를 표준화된 형태&lt;b&gt;(JSON)&lt;/b&gt;로 기록&lt;/li&gt;
&lt;li&gt;다양한 시스템 간 학습 데이터 통합&lt;/li&gt;
&lt;li&gt;**&lt;a href=&quot;https://hansol0607.tistory.com/79&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;LRS (Learning Record Store)&lt;/a&gt;**와 연계하여 데이터 저장 및 분석&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;2. xAPI의 필요성과 발전 배경&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;⚡ &lt;b&gt;SCORM의 한계&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브라우저 기반 학습에만 초점&lt;/li&gt;
&lt;li&gt;오프라인 학습 추적 불가&lt;/li&gt;
&lt;li&gt;코스 완료 여부 및 점수 외에는 추가적인 학습 활동 추적 어려움&lt;/li&gt;
&lt;li&gt;다른 시스템 간 데이터 교환이 어려움&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  &lt;b&gt;xAPI의 혁신적 기능&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;온라인/오프라인 학습 추적&lt;/b&gt;: 현장 교육, 모바일 학습, IoT 기반 학습도 추적 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;플랫폼 독립성&lt;/b&gt;: 다양한 디바이스와 시스템에서 학습 데이터 수집&lt;/li&gt;
&lt;li&gt;&lt;b&gt;유연한 데이터 구조&lt;/b&gt;: 표준화된 JSON 포맷으로 다양한 학습 활동 기록&lt;/li&gt;
&lt;li&gt;&lt;b&gt;LRS와의 통합&lt;/b&gt;: 데이터를 중앙화하여 분석 및 개인화된 학습 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;3. xAPI 진술문(Statement) 구조&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;xAPI는 학습 경험을 &lt;b&gt;진술문(Statement)&lt;/b&gt; 형식으로 기록합니다.&lt;br /&gt;진술문은 기본적으로 다음의 &lt;b&gt;3가지 핵심 요소&lt;/b&gt;를 포함합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  &lt;b&gt;A. 기본 구조: Actor - Verb - Object&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;[Actor(누가)] + [Verb(무엇을 했다)] + [Object(무엇을 했는지)]
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;✅ &lt;b&gt;B. JSON 포맷 예제&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;actor&quot;: {
    &quot;name&quot;: &quot;홍길동&quot;,
    &quot;mbox&quot;: &quot;mailto:honggildong@example.com&quot;
  },
  &quot;verb&quot;: {
    &quot;id&quot;: &quot;http://adlnet.gov/expapi/verbs/completed&quot;,
    &quot;display&quot;: { &quot;ko&quot;: &quot;완료했다&quot; }
  },
  &quot;object&quot;: {
    &quot;id&quot;: &quot;http://example.com/courses/python-basic&quot;,
    &quot;definition&quot;: {
      &quot;name&quot;: { &quot;ko&quot;: &quot;파이썬 기초 과정&quot; },
      &quot;description&quot;: { &quot;ko&quot;: &quot;파이썬 프로그래밍의 기초 개념을 학습하는 과정입니다.&quot; }
    }
  },
  &quot;timestamp&quot;: &quot;2025-02-26T12:00:00Z&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt; ️ &lt;b&gt;C. 주요 필드 설명&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;actor&lt;/b&gt;: 학습 활동을 수행한 사람 또는 시스템&lt;/li&gt;
&lt;li&gt;&lt;b&gt;verb&lt;/b&gt;: 수행된 동작 (예: 완료했다, 시청했다, 제출했다)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;object&lt;/b&gt;: 동작의 대상 (예: 과정, 시험, 문서)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;timestamp&lt;/b&gt;: 학습 활동 발생 시간&lt;/li&gt;
&lt;li&gt;&lt;b&gt;result&lt;/b&gt; (선택적): 점수, 성공 여부, 진행률 등의 결과 데이터&lt;/li&gt;
&lt;li&gt;&lt;b&gt;context&lt;/b&gt; (선택적): 학습 활동의 맥락 (환경, 코스, 팀 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;4. xAPI의 데이터 전송 및 저장 방식&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  &lt;b&gt;A. 데이터 전송 과정&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;학습 활동 발생&lt;/b&gt;: 클라이언트(웹, 모바일 앱, IoT 장치 등)에서 학습 활동 수행&lt;/li&gt;
&lt;li&gt;&lt;b&gt;xAPI 진술문 생성&lt;/b&gt;: JSON 형태의 진술문 생성&lt;/li&gt;
&lt;li&gt;&lt;b&gt;API 호출&lt;/b&gt;: HTTP(S) 기반의 RESTful API를 통해 &lt;b&gt;LRS&lt;/b&gt;로 전송
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;POST /xAPI/statements 엔드포인트 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;LRS 저장&lt;/b&gt;: 전송된 데이터를 &lt;b&gt;LRS&lt;/b&gt;의 데이터베이스에 저장&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  &lt;b&gt;B. Python을 활용한 데이터 전송 예제&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import requests
import json
from requests.auth import HTTPBasicAuth

lrs_endpoint = 'https://lrs.example.com/xAPI/statements'
username = 'api_key'
password = 'api_secret'

statement = {
    &quot;actor&quot;: {&quot;name&quot;: &quot;홍길동&quot;, &quot;mbox&quot;: &quot;mailto:honggildong@example.com&quot;},
    &quot;verb&quot;: {&quot;id&quot;: &quot;http://adlnet.gov/expapi/verbs/completed&quot;, &quot;display&quot;: {&quot;ko&quot;: &quot;완료했다&quot;}},
    &quot;object&quot;: {&quot;id&quot;: &quot;http://example.com/courses/python-basic&quot;}
}

response = requests.post(
    lrs_endpoint,
    data=json.dumps(statement),
    headers={'Content-Type': 'application/json'},
    auth=HTTPBasicAuth(username, password)
)

if response.status_code in [200, 204]:
    print(&quot;✅ 데이터 적재 성공:&quot;, response.json())
else:
    print(&quot;❌ 데이터 적재 실패:&quot;, response.status_code, response.text)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt; ️ &lt;b&gt;5. LRS와의 통합 및 데이터 저장&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;**LRS(Learning Record Store)**는 xAPI 데이터를 저장하고 관리하는 &lt;b&gt;데이터 저장소 역할&lt;/b&gt;을 합니다.&lt;/li&gt;
&lt;li&gt;xAPI는 데이터를 &lt;b&gt;JSON&lt;/b&gt; 형태로 &lt;b&gt;LRS&lt;/b&gt;에 전송하고, &lt;b&gt;MongoDB&lt;/b&gt;, &lt;b&gt;PostgreSQL&lt;/b&gt; 등의 데이터베이스에 저장됩니다.&lt;/li&gt;
&lt;li&gt;저장된 데이터는 REST API를 통해 언제든지 조회할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  &lt;b&gt;LRS 아키텍처&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;801&quot; data-origin-height=&quot;699&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qoWuc/btsMxqkbEY9/pTRWCBfkWHetHCfGDdYnmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qoWuc/btsMxqkbEY9/pTRWCBfkWHetHCfGDdYnmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qoWuc/btsMxqkbEY9/pTRWCBfkWHetHCfGDdYnmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqoWuc%2FbtsMxqkbEY9%2FpTRWCBfkWHetHCfGDdYnmK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;285&quot; height=&quot;249&quot; data-origin-width=&quot;801&quot; data-origin-height=&quot;699&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;6. xAPI와 SCORM의 차이점&lt;/b&gt;&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;비교 항목&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;SCORM&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;xAPI&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;데이터 추적 범위&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;웹 기반 학습에 국한&lt;/td&gt;
&lt;td&gt;온라인/오프라인 모든 학습 경험 추적&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;인터넷 연결 여부&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;항상 연결 필요&lt;/td&gt;
&lt;td&gt;오프라인 학습 후 동기화 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;플랫폼 독립성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;LMS에 종속&lt;/td&gt;
&lt;td&gt;다양한 시스템 및 장치와 통합 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;유연성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;제한적 (진도, 점수 등)&lt;/td&gt;
&lt;td&gt;맞춤형 학습 경험과 데이터 추적 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;데이터 구조&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;XML 기반&lt;/td&gt;
&lt;td&gt;JSON 기반&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;확장성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;낮음&lt;/td&gt;
&lt;td&gt;매우 높음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;7. xAPI 활용 사례&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  &lt;b&gt;A. 기업 교육&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;직원들의 온라인 학습, 현장 실습, 세미나 참여 등을 추적&lt;/li&gt;
&lt;li&gt;교육 효과 분석을 통해 개인별 맞춤형 학습 경로 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  &lt;b&gt;B. 교육 기관&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;학생들의 강의 수강, 과제 제출, 팀 프로젝트 참여 기록&lt;/li&gt;
&lt;li&gt;학습 성과를 기반으로 개별 피드백 및 평가 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  &lt;b&gt;C. 게임 기반 학습&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;학습 게임 내에서 학습자의 활동과 성취도를 기록&lt;/li&gt;
&lt;li&gt;실시간 피드백을 통해 학습 동기 부여&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  &lt;b&gt;D. 헬스케어 및 안전 교육&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;의료 시뮬레이션 및 현장 안전 교육의 진행 상황 추적&lt;/li&gt;
&lt;li&gt;자격 취득 및 현장 수행 능력 분석&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;8. 보안 및 인증&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;전송 보안&lt;/b&gt;: HTTPS를 통한 데이터 암호화&lt;/li&gt;
&lt;li&gt;&lt;b&gt;인증 방식&lt;/b&gt;: OAuth 2.0, Basic Auth, API Key&lt;/li&gt;
&lt;li&gt;&lt;b&gt;접근 제어&lt;/b&gt;: LRS에서 사용자 역할 기반 권한 부여&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;9. 오픈 소스 및 상용 도구와의 연계&lt;/b&gt;&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt; 도구/플랫폼 &lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt; 설명 &lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt; 기술 스택 &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://learninglocker.atlassian.net/wiki/spaces/DOCS/overview&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;Learning Locker&lt;/b&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;가장 널리 사용되는 오픈 소스 LRS&lt;/td&gt;
&lt;td&gt;Node.js + MongoDB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;GrassBlade LRS&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;워드프레스와 통합 가능한 LRS&lt;/td&gt;
&lt;td&gt;PHP + MySQL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Watershed LRS&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;대규모 기업용 상용 LRS&lt;/td&gt;
&lt;td&gt;AWS 기반 클라우드&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Rustici LRS&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;xAPI 표준 개발 업체 제공 상용 LRS&lt;/td&gt;
&lt;td&gt;다양한 언어 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;10. xAPI의 장점 요약&lt;/b&gt;&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;   특징 &lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;   설명 &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;유연한 데이터 추적&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;오프라인 학습, 모바일 학습, 게임, IoT 학습 데이터까지 추적 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;플랫폼 독립성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;다양한 시스템과 장치에서 작동&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;실시간 데이터 전송&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;학습 활동이 발생하는 즉시 데이터 기록&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;보안성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;안전한 데이터 전송 및 저장 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;확장성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;다양한 학습 경험을 지원할 수 있도록 손쉬운 확장 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✨ &lt;b&gt;11. 결론&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;**xAPI(Experience API)**는 학습자의 &lt;b&gt;모든 학습 경험을 표준화된 JSON 포맷으로 기록&lt;/b&gt;하여 &lt;b&gt;LRS&lt;/b&gt;에 저장하고, 이를 통해 학습 데이터를 분석하고 개인화된 학습 경험을 제공할 수 있도록 지원합니다.&lt;/li&gt;
&lt;li&gt;기존 &lt;b&gt;SCORM&lt;/b&gt;의 한계를 극복하여 &lt;b&gt;플랫폼 독립성&lt;/b&gt;, &lt;b&gt;유연성&lt;/b&gt;, &lt;b&gt;실시간 데이터 전송&lt;/b&gt;을 실현하며, &lt;b&gt;기업&lt;/b&gt;, &lt;b&gt;교육기관&lt;/b&gt;, &lt;b&gt;의료&lt;/b&gt;, &lt;b&gt;헬스케어&lt;/b&gt; 등 다양한 산업군에서 활용됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;LRS&lt;/b&gt;와의 통합을 통해 학습 데이터의 중앙 집중화, 분석, 개인 맞춤형 학습 경로 제공 등 데이터 기반 학습 혁신을 가능하게 합니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>용어, 개념</category>
      <category>LRS</category>
      <category>xapi</category>
      <author>HanSol_Lim</author>
      <guid isPermaLink="true">https://hansol0607.tistory.com/78</guid>
      <comments>https://hansol0607.tistory.com/78#entry78comment</comments>
      <pubDate>Wed, 26 Feb 2025 14:17:31 +0900</pubDate>
    </item>
    <item>
      <title>논문 읽기, 어떻게 시작할까? 단계별 접근법</title>
      <link>https://hansol0607.tistory.com/76</link>
      <description>&lt;h3 data-end=&quot;25&quot; data-start=&quot;0&quot; data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;논문 많이 효과적이게 읽는 방법&lt;/b&gt;&lt;/h3&gt;
&lt;p data-end=&quot;99&quot; data-start=&quot;27&quot; data-ke-size=&quot;size16&quot;&gt;논문을 읽는 방법은 자신의 이해도에 따라 달라질 수 있습니다. 특히 &lt;b&gt;초심자&lt;/b&gt;라면 다음과 같은 단계적 접근이 효과적입니다.&lt;/p&gt;
&lt;hr data-end=&quot;104&quot; data-start=&quot;101&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;137&quot; data-start=&quot;106&quot; data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;1. 관심 분야의 좋은 논문 찾기&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;467&quot; data-start=&quot;138&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;189&quot; data-start=&quot;138&quot;&gt;&lt;b&gt;관심 키워드로 논문 검색&lt;/b&gt;: 우선, 관심 있는 주제와 관련된 논문을 검색합니다.&lt;/li&gt;
&lt;li data-end=&quot;268&quot; data-start=&quot;190&quot;&gt;&lt;b&gt;Related Works 리스트업&lt;/b&gt;: 논문의 &lt;b&gt;관련 연구(Related Works)&lt;/b&gt; 부분에서 참고된 논문들을 목록화합니다.&lt;/li&gt;
&lt;li data-end=&quot;337&quot; data-start=&quot;269&quot;&gt;&lt;b&gt;반복적으로 인용되는 논문 파악&lt;/b&gt;: 다양한 논문에서 반복적으로 언급되는 &lt;b&gt;핵심 논문&lt;/b&gt;들이 보이기 시작합니다.&lt;/li&gt;
&lt;li data-end=&quot;467&quot; data-start=&quot;338&quot;&gt;&lt;b&gt;랜드마크 논문 10편 내외 선정&lt;/b&gt;: 이렇게 발견된, &lt;b&gt;레퍼런스가 많이 된 논문 10편 내외를&lt;/b&gt;&amp;nbsp;정리합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;467&quot; data-start=&quot;403&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;467&quot; data-start=&quot;403&quot;&gt;  이 논문들은 해당 분야에서 검증된 고품질 논문으로, 전체적인 맥락을 이해하는 데 중요한 자료입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;472&quot; data-start=&quot;469&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;504&quot; data-start=&quot;474&quot; data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;2. 문제 해결 접근 방식 이해&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-end=&quot;522&quot; data-start=&quot;505&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;초기 단계&lt;/b&gt;:&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;667&quot; data-start=&quot;523&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;667&quot; data-start=&quot;523&quot;&gt;&lt;b&gt;인트로덕션과 관련 연구 섹션 집중&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;667&quot; data-start=&quot;552&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;603&quot; data-start=&quot;552&quot;&gt;해당 분야에서 지금까지 &lt;b&gt;어떤 방법이 시도되었는지(approach)&lt;/b&gt;&amp;nbsp;큰 방향성을 파악합니다.&lt;/li&gt;
&lt;li data-end=&quot;667&quot; data-start=&quot;606&quot;&gt;반복적으로 언급되는 방법론과 개념을 접하면서 **&quot;어느 정도 대충 알겠다&quot;**는 감각이 들 때까지 읽습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-end=&quot;686&quot; data-start=&quot;669&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;중기 단계&lt;/b&gt;:&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;856&quot; data-start=&quot;687&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;856&quot; data-start=&quot;687&quot;&gt;&lt;b&gt;방법론(Methodology)에 집중&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;856&quot; data-start=&quot;718&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;740&quot; data-start=&quot;718&quot;&gt;&lt;b&gt;더 이상 인트로덕션과 관련 연구는 건너뛰어도 됩니다. (&lt;b&gt;이미 충분히 숙지했기 때문입니다.&lt;/b&gt;) &lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;740&quot; data-start=&quot;718&quot;&gt;&lt;b&gt;문제를 어떻게 정의했는지&lt;/b&gt;,&lt;/li&gt;
&lt;li data-end=&quot;763&quot; data-start=&quot;743&quot;&gt;&lt;b&gt;어떤 기술을 사용했고&lt;/b&gt;,&lt;/li&gt;
&lt;li data-end=&quot;795&quot; data-start=&quot;766&quot;&gt;&lt;b&gt;문제를 어떻게 해결했는지&lt;/b&gt;를 분석합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;861&quot; data-start=&quot;858&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;892&quot; data-start=&quot;863&quot; data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;3. 숙련 단계: 한계점 분석&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;1094&quot; data-start=&quot;893&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;949&quot; data-start=&quot;893&quot;&gt;대부분의 논문은 &lt;b&gt;자신들의 연구 한계(Limitation)&lt;/b&gt; 를 명확히 기술하지 않습니다.&lt;/li&gt;
&lt;li data-end=&quot;1032&quot; data-start=&quot;950&quot;&gt;이제는 논문을 읽으며, &lt;b&gt;&quot;이 방법의 한계는 무엇일까?&quot;&lt;/b&gt;, **&quot;어떤 점이 개선될 수 있을까?&quot;**를 스스로 고민하는 것이 중요합니다.&lt;/li&gt;
&lt;li data-end=&quot;1094&quot; data-start=&quot;1033&quot;&gt;이러한 비판적 사고가 연구자에게 필요한 &lt;b&gt;심층적 이해&lt;/b&gt;와 &lt;b&gt;독창적인 연구 방향성&lt;/b&gt;을 제공합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;1099&quot; data-start=&quot;1096&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1118&quot; data-start=&quot;1101&quot; data-ke-size=&quot;size23&quot;&gt;✨ &lt;b&gt;핵심 요약&lt;/b&gt;&lt;/h3&gt;
&lt;p data-end=&quot;1239&quot; data-start=&quot;1119&quot; data-ke-size=&quot;size16&quot;&gt;1️⃣ &lt;b&gt;초기&lt;/b&gt;: 분야 맥락 파악 &amp;rarr; &lt;b&gt;인트로+관련 연구 집중&lt;/b&gt;&lt;br /&gt;2️⃣ &lt;b&gt;중기&lt;/b&gt;: 문제 해결 방식 분석 &amp;rarr; &lt;b&gt;방법론 집중&lt;/b&gt;&lt;br /&gt;3️⃣ &lt;b&gt;숙련&lt;/b&gt;: 한계점 탐구 &amp;rarr; &lt;b&gt;비판적 분석 능력 향상&lt;/b&gt;&lt;/p&gt;
&lt;hr data-end=&quot;1244&quot; data-start=&quot;1241&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;1332&quot; data-start=&quot;1246&quot; data-ke-size=&quot;size16&quot;&gt;이러한 단계적 접근 방식을 통해 논문을 읽는 과정이 단순한 독해가 아니라, &lt;b&gt;비판적 사고와 연구 역량을 키우는 경험&lt;/b&gt;이 될 것입니다.  &lt;/p&gt;</description>
      <category>전략, 방법, 지름길</category>
      <category>논문</category>
      <author>HanSol_Lim</author>
      <guid isPermaLink="true">https://hansol0607.tistory.com/76</guid>
      <comments>https://hansol0607.tistory.com/76#entry76comment</comments>
      <pubDate>Mon, 24 Feb 2025 13:21:28 +0900</pubDate>
    </item>
    <item>
      <title>백준 17413번: 단어 뒤집기 2 (Python)</title>
      <link>https://hansol0607.tistory.com/75</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;0. 문제 이해&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  &lt;b&gt;문제 설명&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열 S가 주어졌을 때, &lt;b&gt;단어만 뒤집고 태그는 그대로 출력&lt;/b&gt;하는 프로그램을 작성하는 문제입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  &lt;b&gt;문제의 주요 규칙&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;단어&lt;/b&gt;: 알파벳 소문자(a-z)와 숫자(0-9)로 구성.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;태그&lt;/b&gt;: &amp;lt;로 시작하고 &amp;gt;로 끝나는 부분 문자열로, &lt;b&gt;태그 내부는 뒤집지 않고 그대로 출력&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;공백&lt;/b&gt;: 단어 구분자이며, 단어마다 공백이 하나씩 존재.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;태그 처리&lt;/b&gt;: &amp;lt;를 만나면 태그 모드로 전환하여 태그 안의 문자를 그대로 출력하고, &amp;gt;를 만나면 태그 모드를 해제.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;단어 처리&lt;/b&gt;: 태그 외부에서는 단어를 &lt;b&gt;역순으로 출력&lt;/b&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;⚡ &lt;b&gt;중요한 추가 사항&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문자열의 길이는 최대 100,000으로, **O(N)**의 시간 복잡도를 가지는 풀이가 필요합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;마지막 단어 출력&lt;/b&gt;을 위해 반복문 종료 후 스택에 남은 문자를 출력하는 추가 처리도 필요합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;1. 핵심 (문제를 풀기 위한 접근 전략)&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  &lt;b&gt;1️⃣ 태그 구간 판별&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;lt;를 만나면 태그 모드를 &lt;b&gt;True&lt;/b&gt;, &amp;gt;를 만나면 &lt;b&gt;False&lt;/b&gt;로 변경합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;태그 모드가 True인 동안&lt;/b&gt;은 모든 문자를 그대로 출력합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  &lt;b&gt;2️⃣ 단어 역순 출력 (Stack 사용)&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;태그 외부&lt;/b&gt;에서 단어를 한 글자씩 **스택(stack)**에 넣습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;공백(' ')&lt;/b&gt; 또는 **태그 시작(&amp;lt;)**을 만나면 스택의 모든 문자를 pop()하여 역순으로 출력합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;반복문이 끝난 후&lt;/b&gt;에도 스택에 남아 있는 문자가 있다면 &lt;b&gt;마지막 단어를 출력&lt;/b&gt;합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  &lt;b&gt;3️⃣ 공백 처리&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공백을 만나면 스택의 문자를 모두 출력한 후 공백을 출력합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  &lt;b&gt;4️⃣ 시간 복잡도 고려&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 문자를 한 번씩만 탐색하고, 스택 연산은 O(1)로 처리하여 **O(N)**의 시간 복잡도를 유지합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;2. 내 코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# 핵심 2가지
# 1. 태그(&amp;lt;&amp;gt;) 시작점과 끝 점의 flag 설정
# 2. &quot;순서를 거꾸로&quot; =&amp;gt; stack =&amp;gt; stack.pop()

s = input()
stack = []
tag_flag = False

for char in s:
    
    # 태그 시작
    if char == &quot;&amp;lt;&quot;:    
        tag_flag = True
        while stack:    # 태그를 만나기 전까지 스택에 쌓아온 게 있다면 pop
            print(stack.pop(), end='')
        print(char, end='')
        
    # 태그 끝
    elif char == &quot;&amp;gt;&quot;:    
        tag_flag = False
        print(char, end='')
        
    # 태그 내부
    elif tag_flag:    
        print(char, end='')
        
    # 태그 외부 + 공백
    elif char == &quot; &quot;:
        while stack:
            print(stack.pop(), end='')
        print(&quot; &quot;, end='')
        
    # 태그 외부 (단어 구성 중)
    else:
        stack.append(char)
        
# 마지막 단어(태그 외부 + 공백X = 스택에 쌓여있는 거)
while stack:
    print(stack.pop(), end='')&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;✨ 코드 특징 요약 ✨&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;✅ &lt;b&gt;태그 내부와 외부를 정확히 구분하여 처리&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;✅ &lt;b&gt;스택을 이용한 단어 역순 출력&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;✅ &lt;b&gt;O(N) 시간 복잡도로 효율성 확보&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>알고리즘, 코딩테스트</category>
      <category>백준</category>
      <category>스택</category>
      <category>알고리즘</category>
      <category>코딩테스트</category>
      <category>파이썬</category>
      <author>HanSol_Lim</author>
      <guid isPermaLink="true">https://hansol0607.tistory.com/75</guid>
      <comments>https://hansol0607.tistory.com/75#entry75comment</comments>
      <pubDate>Mon, 24 Feb 2025 11:02:28 +0900</pubDate>
    </item>
    <item>
      <title>파이썬 deque</title>
      <link>https://hansol0607.tistory.com/74</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;파이썬 deque 총정리&lt;/b&gt;&lt;/h3&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;0. 개요&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;**deque (Double-Ended Queue)**는 파이썬의 collections 모듈에서 제공하는 자료구조입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;양쪽 끝에서의 O(1) 삽입과 삭제&lt;/b&gt;가 가능하여 큐(queue)나 스택(stack) 구현 시 **리스트(list)**보다 &lt;b&gt;효율적&lt;/b&gt;입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;1. deque의 주요 특징&lt;/b&gt;&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;특징&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;양방향 접근성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;앞쪽과 뒤쪽 모두에서 **O(1)**의 시간으로 추가 및 삭제 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;빠른 삭제/삽입&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;리스트는 앞쪽 요소 삭제 시 &lt;b&gt;O(N)&lt;/b&gt; 시간이 소요되지만, deque는 &lt;b&gt;O(1)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;스레드 안전성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;멀티스레드 환경&lt;/b&gt;에서 안정적으로 작동 (락 제공)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;메모리 효율적&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;동적 배열보다 &lt;b&gt;메모리 사용량이 적음&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;2. deque 생성과 초기화&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;from collections import deque

# 비어있는 deque 생성
dq = deque()

# 리스트로부터 deque 생성
dq = deque([1, 2, 3, 4, 5])

# 최대 길이 지정 (초과 시 자동으로 오래된 요소 제거)
dq = deque([1, 2, 3], maxlen=5)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✍ &lt;b&gt;3. deque의 주요 메서드 및 사용 예제&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  &lt;b&gt;삽입 및 삭제 메서드&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 100px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;메서드&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;예제&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;append(x)&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;맨 오른쪽(뒤)에 &lt;b&gt;x 추가&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;dq.append(10)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;appendleft(x)&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;맨 왼쪽(앞)에 &lt;b&gt;x 추가&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;dq.appendleft(5)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;pop()&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;맨 오른쪽(뒤)의 &lt;b&gt;요소 제거 및 반환&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;dq.pop()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;popleft()&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;맨 왼쪽(앞)의 &lt;b&gt;요소 제거 및 반환&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;dq.popleft()&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;dq = deque([2, 3, 4])
dq.append(5)           # [2, 3, 4, 5]
dq.appendleft(1)       # [1, 2, 3, 4, 5]
dq.pop()               # [1, 2, 3, 4]
dq.popleft()           # [2, 3, 4]
print(dq)              # deque([2, 3, 4])
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  &lt;b&gt;요소 회전 메서드&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;메서드&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;예제&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rotate(n=1)&lt;/td&gt;
&lt;td&gt;양수면 &lt;b&gt;오른쪽&lt;/b&gt;으로 n칸, 음수면 &lt;b&gt;왼쪽&lt;/b&gt;으로 n칸 이동&lt;/td&gt;
&lt;td&gt;dq.rotate(2)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;dq = deque([1, 2, 3, 4, 5])
dq.rotate(2)             # deque([4, 5, 1, 2, 3])
dq.rotate(-3)            # deque([2, 3, 4, 5, 1])
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  &lt;b&gt;인덱스 및 검색 메서드&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;메서드&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;예제&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;index(x)&lt;/td&gt;
&lt;td&gt;&lt;b&gt;x 값의 첫 번째 인덱스 반환&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;dq.index(3)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;count(x)&lt;/td&gt;
&lt;td&gt;&lt;b&gt;x 값의 개수 반환&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;dq.count(2)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;reverse()&lt;/td&gt;
&lt;td&gt;&lt;b&gt;deque 요소 뒤집기&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;dq.reverse()&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;dq = deque([1, 2, 3, 2, 4])
print(dq.index(2))        # 1
print(dq.count(2))        # 2
dq.reverse()              # deque([4, 2, 3, 2, 1])
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  &lt;b&gt;기타 메서드&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;메서드&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;예제&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;clear()&lt;/td&gt;
&lt;td&gt;&lt;b&gt;모든 요소 제거&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;dq.clear()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;extend(iter)&lt;/td&gt;
&lt;td&gt;&lt;b&gt;iter의 모든 요소를 뒤쪽에 추가&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;dq.extend([6, 7])&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;extendleft(iter)&lt;/td&gt;
&lt;td&gt;&lt;b&gt;iter의 모든 요소를 앞쪽에 추가 (역순)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;dq.extendleft([0, -1])&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;dq = deque([3, 4, 5])
dq.extend([6, 7])         # deque([3, 4, 5, 6, 7])
dq.extendleft([2, 1])     # deque([1, 2, 3, 4, 5, 6, 7])
dq.clear()                # deque([])
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;4. deque vs list 성능 비교&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업 list 성능 deque 성능&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;작업&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;list 성능&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;deque 성능&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;앞쪽 삽입/삭제&lt;/td&gt;
&lt;td&gt;&lt;b&gt;O(N)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;O(1)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;뒤쪽 삽입/삭제&lt;/td&gt;
&lt;td&gt;&lt;b&gt;O(1)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;O(1)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;중간 요소 접근&lt;/td&gt;
&lt;td&gt;&lt;b&gt;O(1)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;O(N)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;순회 (iteration)&lt;/td&gt;
&lt;td&gt;빠름&lt;/td&gt;
&lt;td&gt;빠름&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  결론:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터의 &lt;b&gt;앞뒤 양방향 처리&lt;/b&gt;가 필요한 경우 &lt;b&gt;deque&lt;/b&gt;가 &lt;b&gt;list&lt;/b&gt;보다 &lt;b&gt;성능이 뛰어남&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;스택&lt;/b&gt;과 &lt;b&gt;큐&lt;/b&gt; 구현 시 &lt;b&gt;deque&lt;/b&gt; 사용 권장&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;5. 실전 예제: 요세푸스 문제&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;from collections import deque

n, k = map(int, input().split())
people = deque(range(1, n + 1))
result = []

while people:
    people.rotate(-(k - 1))     # k번째 사람이 앞으로 오도록 회전
    result.append(people.popleft())  # k번째 사람 제거

print(&quot;&amp;lt;&quot; + &quot;, &quot;.join(map(str, result)) + &quot;&amp;gt;&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;요세푸스 문제 해결&lt;/b&gt;: &lt;b&gt;rotate() + popleft()&lt;/b&gt; 조합으로 &lt;b&gt;O(N)&lt;/b&gt; 시간에 해결&lt;/li&gt;
&lt;li&gt;&lt;b&gt;효율적 인덱스 관리&lt;/b&gt;와 &lt;b&gt;가독성 높은 코드&lt;/b&gt; 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;6. deque 사용 시 주의사항&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;랜덤 접근&lt;/b&gt;(중간 인덱스 접근)이 필요할 경우 &lt;b&gt;리스트&lt;/b&gt;가 더 적합&lt;/li&gt;
&lt;li&gt;&lt;b&gt;회전 연산&lt;/b&gt;(rotate()) 시 &lt;b&gt;양수/음수 방향&lt;/b&gt; 주의&lt;/li&gt;
&lt;li&gt;&lt;b&gt;스레드 안전성&lt;/b&gt; 제공하지만, 멀티프로세싱 환경에서는 추가 고려 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;7. deque 사용이 적합한 상황&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1️⃣ &lt;b&gt;큐(Queue)&lt;/b&gt; 구현: popleft()로 효율적 앞쪽 제거&lt;br /&gt;2️⃣ &lt;b&gt;스택(Stack)&lt;/b&gt; 구현: append()/pop() 사용&lt;br /&gt;3️⃣ &lt;b&gt;슬라이딩 윈도우 문제&lt;/b&gt; 해결&lt;br /&gt;4️⃣ &lt;b&gt;요세푸스 문제&lt;/b&gt; 등 원형 데이터 처리&lt;br /&gt;5️⃣ &lt;b&gt;회전 기반 연산&lt;/b&gt;(rotate) 사용이 필요한 경우&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;8. 결론&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;  &lt;b&gt;deque는 큐와 스택 작업에 최적화된 자료구조&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;✅ **O(1)**의 빠른 앞/뒤 삽입 및 삭제&lt;/li&gt;
&lt;li&gt;⚡ &lt;b&gt;rotate()&lt;/b&gt;, &lt;b&gt;popleft()&lt;/b&gt;, &lt;b&gt;appendleft()&lt;/b&gt; 등의 메서드를 통해 &lt;b&gt;강력한 기능 제공&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;  &lt;b&gt;리스트보다 효율적이고 메모리 절약적인 대안&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;  &lt;b&gt;대규모 데이터 처리 및 알고리즘 문제 해결 시 필수 도구&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;&quot;앞뒤 양방향 처리와 회전이 필요한 작업에서는 언제나 deque가 최고의 선택입니다!&quot;&lt;/b&gt;  ✨&lt;/p&gt;</description>
      <category>알고리즘, 코딩테스트</category>
      <category>deque</category>
      <category>알고리즘</category>
      <category>코딩테스트</category>
      <category>파이썬</category>
      <author>HanSol_Lim</author>
      <guid isPermaLink="true">https://hansol0607.tistory.com/74</guid>
      <comments>https://hansol0607.tistory.com/74#entry74comment</comments>
      <pubDate>Mon, 24 Feb 2025 09:28:58 +0900</pubDate>
    </item>
    <item>
      <title>백준 10845번: 큐 (Python)</title>
      <link>https://hansol0607.tistory.com/73</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;0. 문제 이해&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  문제 설명:&lt;/b&gt;&lt;br /&gt;정수를 저장하는 **큐(Queue)**를 구현하고, 주어지는 명령어를 처리하는 프로그램 작성.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  지원해야 하는 명령어:&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;push X: 정수 X를 큐에 넣음&lt;/li&gt;
&lt;li&gt;pop: 큐에서 가장 앞의 정수를 제거하고 출력 (없으면 -1 출력)&lt;/li&gt;
&lt;li&gt;size: 큐에 들어있는 정수의 개수 출력&lt;/li&gt;
&lt;li&gt;empty: 큐가 비어있으면 1, 아니면 0 출력&lt;/li&gt;
&lt;li&gt;front: 큐의 가장 앞 정수를 출력 (없으면 -1 출력)&lt;/li&gt;
&lt;li&gt;back: 큐의 가장 뒤 정수를 출력 (없으면 -1 출력)&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  입력 조건:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;명령의 수 N (1 &amp;le; N &amp;le; 10,000)&lt;/li&gt;
&lt;li&gt;각 명령어는 문제에서 제시한 형태로 주어짐&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  핵심 요구사항:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;시간 초과 없이&lt;/b&gt; 큐 연산 수행&lt;/li&gt;
&lt;li&gt;각 명령어 수행 시 결과를 정확히 출력&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;1. 내 코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;import sys

n = int(sys.stdin.readline())
que = []

for _ in range(n):
    cmd = sys.stdin.readline().strip()
    
    if cmd.startswith(&quot;push&quot;):
        _, x = cmd.split()
        que.append(x)
    elif cmd == &quot;pop&quot;:
        if len(que) &amp;gt; 0:
            print(que[0])
            que.pop(0)
        else:
            print(-1)
    elif cmd == &quot;size&quot;:
        print(len(que))
    elif cmd == &quot;empty&quot;:
        if len(que) == 0:
            print(1)
        else:
            print(0)
    elif cmd == &quot;front&quot;:
        if len(que) &amp;gt; 0:
            print(que[0])
        else:
            print(-1)
    elif cmd == &quot;back&quot;:
        if len(que) &amp;gt; 0:
            print(que[-1])
        else:
            print(-1)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;⚡ &lt;b&gt;2. 문제점 및 개선사항&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  &lt;b&gt;문제점 분석&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1️⃣ &lt;b&gt;시간 복잡도 문제&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;list.pop(0) 사용 시 &lt;b&gt;O(N)&lt;/b&gt; 연산 발생&lt;/li&gt;
&lt;li&gt;큐의 앞쪽 요소를 제거할 때마다 모든 요소가 앞으로 이동&lt;/li&gt;
&lt;li&gt;입력이 많을 경우 &lt;b&gt;시간 초과&lt;/b&gt; 발생 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2️⃣ &lt;b&gt;출력 타입 일관성 문제&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;append(x) 시 문자열 형태로 저장됨&lt;/li&gt;
&lt;li&gt;출력 시 정수형이 요구될 수 있어 &lt;b&gt;int(x) 변환 필요&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3️⃣ &lt;b&gt;조건문 간소화 부족&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;if len(que) &amp;gt; 0: &amp;rarr; if que: 로 간단히 표현 가능 (파이썬의 빈 컨테이너는 False 평가됨)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  &lt;b&gt;개선사항&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;✅ &lt;b&gt;collections.deque 사용&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;큐 자료구조의 &lt;b&gt;양쪽 끝&lt;/b&gt;에서의 삽입과 삭제를 &lt;b&gt;O(1)&lt;/b&gt; 시간에 처리&lt;/li&gt;
&lt;li&gt;리스트의 pop(0) 대신 &lt;b&gt;popleft()&lt;/b&gt; 사용하여 효율적 연산 수행&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;✅ &lt;b&gt;popleft() 메서드&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;deque에서 &lt;b&gt;왼쪽 끝 요소를 제거하고 반환&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;리스트의 pop(0)와 동일한 기능이지만, &lt;b&gt;O(1)&lt;/b&gt; 시간 복잡도 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;✅ &lt;b&gt;삼항 연산자&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;print(que[0] if que else -1) 형태로 코드 &lt;b&gt;간결화&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;가독성&lt;/b&gt; 향상 및 &lt;b&gt;불필요한 조건문 제거&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;✅ &lt;b&gt;정수형 변환&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력 시 &lt;b&gt;int(x)로 명확하게 형 변환&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;출력 시 일관된 데이터 타입 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;3. 최적화된 코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;from collections import deque
import sys

n = int(sys.stdin.readline())
que = deque()

for _ in range(n):
    cmd = sys.stdin.readline().strip()

    if cmd.startswith(&quot;push&quot;):
        _, x = cmd.split()
        que.append(int(x))  # 정수형 변환
    elif cmd == &quot;pop&quot;:
        print(que.popleft() if que else -1)
    elif cmd == &quot;size&quot;:
        print(len(que))
    elif cmd == &quot;empty&quot;:
        print(0 if que else 1)
    elif cmd == &quot;front&quot;:
        print(que[0] if que else -1)
    elif cmd == &quot;back&quot;:
        print(que[-1] if que else -1)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;4. 최적화된 코드의 특징 (기존 코드와의 비교)&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;항목 기존 코드 최적화된 코드&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;자료구조&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;리스트 (list) 사용&lt;/td&gt;
&lt;td&gt;덱 (collections.deque) 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;pop 연산 시간 복잡도&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;O(N)&lt;/b&gt; (pop(0) 사용)&lt;/td&gt;
&lt;td&gt;&lt;b&gt;O(1)&lt;/b&gt; (popleft() 사용)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;빈 큐 검사&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;if len(que) &amp;gt; 0: 사용&lt;/td&gt;
&lt;td&gt;if que: 사용 (간결)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;출력 데이터 타입&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;문자열 출력 (형변환 없음)&lt;/td&gt;
&lt;td&gt;정수형 출력 (int(x)) 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;코드 가독성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;조건문 반복적 사용&lt;/td&gt;
&lt;td&gt;&lt;b&gt;삼항 연산자&lt;/b&gt;로 간결화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;성능&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;큰 입력 시 시간 초과 가능&lt;/td&gt;
&lt;td&gt;대량 입력에도 안정적 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;5. 결론&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;  **collections.deque**와 &lt;b&gt;popleft()&lt;/b&gt; 사용으로 &lt;b&gt;시간 복잡도 O(1)&lt;/b&gt; 달성&lt;/li&gt;
&lt;li&gt;✅ &lt;b&gt;if que:&lt;/b&gt;, &lt;b&gt;삼항 연산자&lt;/b&gt; 사용으로 &lt;b&gt;간결하고 가독성 높은 코드&lt;/b&gt; 작성&lt;/li&gt;
&lt;li&gt;  &lt;b&gt;정수형 변환&lt;/b&gt;을 통해 출력 데이터 타입 일관성 유지&lt;/li&gt;
&lt;li&gt;⚡ &lt;b&gt;대규모 입력&lt;/b&gt;에도 &lt;b&gt;효율적이고 안정적인 큐 연산 처리&lt;/b&gt; 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;6. 개선 필요 핵심&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;⚡ list.pop(0) 대신 collections.deque.popleft()를 사용하여 큐의 pop 연산을 O(N)에서 O(1)로 최적화하고, if que: 및 삼항 연산자로 코드 가독성과 성능을 개선하자!&lt;/b&gt;  ✨&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>알고리즘, 코딩테스트</category>
      <category>백준</category>
      <category>알고리즘</category>
      <category>코딩테스트</category>
      <category>큐</category>
      <category>파이썬</category>
      <author>HanSol_Lim</author>
      <guid isPermaLink="true">https://hansol0607.tistory.com/73</guid>
      <comments>https://hansol0607.tistory.com/73#entry73comment</comments>
      <pubDate>Fri, 21 Feb 2025 09:56:23 +0900</pubDate>
    </item>
    <item>
      <title>React에서 useRef를 사용하는 이유(let과의 차이)</title>
      <link>https://hansol0607.tistory.com/72</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;⚛️ &lt;b&gt;React에서 useRef를 사용하는 이유&lt;/b&gt;⚛️&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useRef는 React에서 &lt;b&gt;컴포넌트의 상태 변화 없이&lt;/b&gt; &lt;u&gt;값을 유지&lt;/u&gt;하거나, &lt;u&gt;&lt;b&gt;DOM 요소에 직접 접근&lt;/b&gt;&lt;/u&gt;할 수 있도록 하는 **훅(Hook)**입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 사람들이 &quot;let으로도 값을 저장할 수 있는데 왜 굳이 useRef를 사용해야 할까?&quot;라고 의문을 가집니다.&lt;br /&gt;하지만 useRef는 단순한 변수 저장 이상의 기능과 장점을 제공합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;1. useRef와 let의 차이점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 160px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt; 비교 항목 &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt; ⚡ useRef &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;   let &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;height: 40px;&quot;&gt;  &lt;b&gt;렌더링 유지 여부&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 40px;&quot;&gt;값이 변경되어도 &lt;b&gt;컴포넌트를 리렌더링하지 않음&lt;/b&gt;.&lt;/td&gt;
&lt;td style=&quot;height: 40px;&quot;&gt;값이 변경되어도 &lt;b&gt;렌더링 없음&lt;/b&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;♻ &lt;b&gt;렌더링 간 값 유지&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;컴포넌트가 리렌더링되어도 값이 유지됨&lt;/b&gt;.&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;리렌더링 시 값이 초기화됨&lt;/b&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;  &lt;b&gt;DOM 참조 기능&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;DOM 요소 참조&lt;/b&gt; 가능 (ref={myRef} 사용).&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;❌ DOM 참조 불가.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;height: 40px;&quot;&gt;⚡ &lt;b&gt;비동기 및 이벤트 핸들링&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 40px;&quot;&gt;비동기 작업 중 &lt;b&gt;현재 상태 참조&lt;/b&gt; 시 유용.&lt;/td&gt;
&lt;td style=&quot;height: 40px;&quot;&gt;비동기 작업에서 참조 시 &lt;b&gt;최신 값 불일치&lt;/b&gt; 가능성.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;  &lt;b&gt;React 철학과 일치&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;React의 &lt;b&gt;선언적 프로그래밍 방식&lt;/b&gt;과 일치.&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;React와 &lt;b&gt;일반 변수 사용 방식&lt;/b&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;2. useRef를 사용해야 하는 이유&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;✅ &lt;b&gt;1) 리렌더링 간에도 값 유지&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;let은 컴포넌트가 리렌더링될 때 &lt;b&gt;값이 초기화&lt;/b&gt;되지만, useRef는 &lt;b&gt;값을 유지&lt;/b&gt;합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;예시&lt;/b&gt;: 렌더링 간 카운터 유지&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import React, { useRef, useState } from &quot;react&quot;;

function Counter() {
  const countRef = useRef(0); // 리렌더링에도 값 유지
  const [count, setCount] = useState(0); // 상태 기반 렌더링

  const handleClick = () =&amp;gt; {
    countRef.current += 1; // 상태 변경 X, 리렌더링 X
    console.log(`countRef 값: ${countRef.current}`);
    setCount(count + 1);  // 상태 변경으로 리렌더링 발생
  };

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h2&amp;gt;{count}&amp;lt;/h2&amp;gt;
      &amp;lt;button onClick={handleClick}&amp;gt;+1 증가&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default Counter;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;설명&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;countRef.current는 버튼 클릭 시마다 값이 증가하지만 &lt;b&gt;리렌더링을 발생시키지 않음&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;setCount는 &lt;b&gt;리렌더링을 유발&lt;/b&gt;하여 화면이 업데이트됨.&lt;/li&gt;
&lt;li&gt;let으로 선언 시 handleClick 호출 시마다 값이 &lt;b&gt;초기화&lt;/b&gt;되어 증가값이 유지되지 않음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;✅ &lt;b&gt;2) DOM 요소에 직접 접근&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;useRef는 &lt;b&gt;React에서 DOM 요소에 접근&lt;/b&gt;할 때 주로 사용됩니다.&lt;/li&gt;
&lt;li&gt;과거에는 React.createRef()가 사용되었지만, &lt;b&gt;함수형 컴포넌트&lt;/b&gt;에서는 useRef가 표준입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;예시&lt;/b&gt;: 입력 필드에 자동 포커스 설정&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import React, { useRef, useEffect } from &quot;react&quot;;

function AutoFocusInput() {
  const inputRef = useRef(); // input DOM 참조

  useEffect(() =&amp;gt; {
    inputRef.current.focus(); // 컴포넌트 마운트 시 포커스
  }, []);

  return &amp;lt;input ref={inputRef} placeholder=&quot;여기에 입력하세요&quot; /&amp;gt;;
}

export default AutoFocusInput;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;설명&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;inputRef.current는 마운트 시 &lt;b&gt;실제 DOM 요소&lt;/b&gt;를 참조합니다.&lt;/li&gt;
&lt;li&gt;let을 사용하면 DOM에 직접 접근할 수 없기 때문에 동일한 기능을 수행할 수 없습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;✅ &lt;b&gt;3) 비동기 작업에서 최신 상태 참조&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;비동기 콜백&lt;/b&gt;이나 &lt;b&gt;타이머&lt;/b&gt; 사용 시, useRef는 항상 &lt;b&gt;최신 값을 참조&lt;/b&gt;할 수 있도록 합니다.&lt;/li&gt;
&lt;li&gt;반면, let이나 state는 비동기 함수 내에서 &lt;b&gt;이전 값을 참조할 위험&lt;/b&gt;이 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;예시&lt;/b&gt;: 비동기 작업 중 최신 상태 참조&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import React, { useRef, useState, useEffect } from &quot;react&quot;;

function Timer() {
  const [seconds, setSeconds] = useState(0);
  const secondsRef = useRef(seconds); // 최신 상태 참조

  useEffect(() =&amp;gt; {
    secondsRef.current = seconds;
  }, [seconds]);

  useEffect(() =&amp;gt; {
    const interval = setInterval(() =&amp;gt; {
      console.log(`현재 시간: ${secondsRef.current}초`);
      setSeconds((prev) =&amp;gt; prev + 1);
    }, 1000);

    return () =&amp;gt; clearInterval(interval);
  }, []);

  return &amp;lt;h2&amp;gt;{seconds}초 경과&amp;lt;/h2&amp;gt;;
}

export default Timer;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;설명&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;secondsRef.current는 &lt;b&gt;항상 최신 상태의 값&lt;/b&gt;을 참조합니다.&lt;/li&gt;
&lt;li&gt;let을 사용하면 클로저로 인해 &lt;b&gt;초기 값만 참조&lt;/b&gt;하여 시간이 업데이트되지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;✅ &lt;b&gt;4) 상태 변경 없이 값 추적 (렌더링 최적화)&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;렌더링 최적화&lt;/b&gt;가 필요한 경우, useRef를 사용하여 &lt;b&gt;불필요한 렌더링 방지&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;상태(useState) 변경 시마다 &lt;b&gt;컴포넌트가 리렌더링&lt;/b&gt;되지만, useRef는 값이 바뀌어도 &lt;b&gt;렌더링을 트리거하지 않음&lt;/b&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;예시&lt;/b&gt;: 불필요한 리렌더링 방지&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import React, { useRef, useState } from &quot;react&quot;;

function RenderCounter() {
  const renderCount = useRef(0);
  const [value, setValue] = useState(&quot;&quot;);

  renderCount.current += 1; // 리렌더링 횟수 추적

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;input
        value={value}
        onChange={(e) =&amp;gt; setValue(e.target.value)}
      /&amp;gt;
      &amp;lt;p&amp;gt;리렌더링 횟수: {renderCount.current}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default RenderCounter;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;설명&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;renderCount.current는 렌더링 시마다 업데이트되지만 &lt;b&gt;리렌더링을 발생시키지 않음&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;useState가 업데이트될 때만 실제 리렌더링이 발생.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;3. 언제 let 대신 useRef를 사용해야 할까?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ useRef 사용 권장 시점 ⚠ let 사용 가능 시점&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;✔ &lt;b&gt;리렌더링 간 값을 유지&lt;/b&gt;할 때 (setTimeout, setInterval에서 최신 값 참조)&lt;/td&gt;
&lt;td&gt;✔ &lt;b&gt;렌더링마다 초기화되어도 무방한 임시 값&lt;/b&gt; 사용 시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;✔ &lt;b&gt;DOM 요소를 직접 참조&lt;/b&gt;할 때 (focus(), scrollIntoView())&lt;/td&gt;
&lt;td&gt;✔ 컴포넌트 외부에서만 사용하는 단순 변수 처리 시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;✔ &lt;b&gt;렌더링을 유발하지 않고 값 추적&lt;/b&gt;할 때 (불필요한 리렌더링 방지)&lt;/td&gt;
&lt;td&gt;✔ 컴포넌트 내 로컬 연산 처리용 변수 필요 시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;✔ &lt;b&gt;비동기 작업에서 최신 상태 참조&lt;/b&gt;가 필요할 때&lt;/td&gt;
&lt;td&gt;✔ 리렌더링 간 값 유지가 필요 없는 계산 값 저장 시&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;4. 핵심 요약&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1️⃣ &lt;b&gt;리렌더링 간 데이터 유지&lt;/b&gt;: useRef는 &lt;b&gt;리렌더링 후에도 값 유지&lt;/b&gt;, let은 초기화됨.&lt;br /&gt;2️⃣ &lt;b&gt;렌더링 최적화&lt;/b&gt;: useRef는 값이 변경되어도 &lt;b&gt;렌더링 트리거 없음&lt;/b&gt;.&lt;br /&gt;3️⃣ &lt;b&gt;DOM 직접 참조&lt;/b&gt;: useRef는 &lt;b&gt;DOM 요소에 접근&lt;/b&gt;하는 &lt;b&gt;React 표준 방식&lt;/b&gt;.&lt;br /&gt;4️⃣ &lt;b&gt;비동기/동기 문제 해결&lt;/b&gt;: 비동기 작업에서 &lt;b&gt;최신 값 참조&lt;/b&gt;가 필요한 경우 유용.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;한 줄 요약:&lt;/b&gt;&lt;br /&gt;&amp;ldquo;let은 일반 변수 저장 용도이지만, useRef는 &lt;b&gt;React 환경에서 리렌더링과 독립적인 값 유지&lt;/b&gt;, &lt;b&gt;DOM 접근&lt;/b&gt;, &lt;b&gt;렌더링 최적화&lt;/b&gt;를 위한 &lt;b&gt;고유한 역할&lt;/b&gt;을 수행합니다.&amp;rdquo;  ✨&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;⚛️ 함수 밖에 let을 선언하면 useRef와 동일하지 않나요? 리렌더링 되어도 변수 초기화 안되잖아요 ⚛️&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;1. 질문: let을 함수 밖에 선언하면 useRef와 같은가?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아닙니다. &lt;b&gt;컴포넌트 함수 외부의 let은 &lt;u&gt;모든&lt;/u&gt; 컴포넌트 인스턴스에서 &lt;u&gt;공유&lt;/u&gt;&lt;/b&gt;되지만,&lt;br /&gt;&lt;b&gt;useRef는 &lt;u&gt;각&lt;/u&gt; 컴포넌트 인스턴스마다 &lt;u&gt;&lt;b&gt;독립적인 값&lt;/b&gt;을 유지&lt;/u&gt;&lt;/b&gt;합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;✅ &lt;b&gt;긴 답변&lt;/b&gt;:&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴포넌트 함수 &lt;b&gt;외부&lt;/b&gt;에 선언된 let 변수는 &lt;b&gt;모든 컴포넌트 인스턴스가 동일한 값을 공유&lt;/b&gt;합니다.&lt;br /&gt;이는 &lt;u&gt;&lt;b&gt;React의 컴포넌트 분리 원칙과 동작 방식에 어긋나며,&lt;/b&gt; &lt;/u&gt;특히 &lt;b&gt;동적 렌더링&lt;/b&gt;이나 &lt;b&gt;다중 인스턴스 사용&lt;/b&gt; 시 &lt;b&gt;버그&lt;/b&gt;를 유발할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;2. useRef와 함수 외부 let의 차이점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 120px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt; 비교 항목 &lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt; &lt;span style=&quot;text-align: start;&quot;&gt;⚡ useRef&amp;nbsp;&lt;/span&gt; &lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;&lt;b&gt;   함수 외부 let &lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;  &lt;b&gt;스코프(Scope)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;컴포넌트 인스턴스마다 독립적인 스코프&lt;/b&gt;.&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;모든 인스턴스 간 공유되는 전역/모듈 스코프&lt;/b&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;  &lt;b&gt;리렌더링 시 동작&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;리렌더링 시에도 값 유지&lt;/b&gt;.&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;동일. (하지만 모든 인스턴스가 공유함).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;  &lt;b&gt;다중 인스턴스 지원&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;각 인스턴스마다 &lt;b&gt;독립적인 값 유지&lt;/b&gt;.&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;모든 인스턴스가 동일한 값 참조&lt;/b&gt; (버그 위험).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;  &lt;b&gt;React 철학 준수&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;선언적 프로그래밍과 상태 격리&lt;/b&gt; 지원.&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;React 상태 관리 방식과 &lt;b&gt;불일치&lt;/b&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;  &lt;b&gt;실제 사용 시나리오&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;폼 상태 유지, DOM 참조, 비동기 최신 상태 참조.&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;주로 모듈 스코프 상수 또는 설정 값 저장.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;3. 예제 코드로 보는 차이점&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  &lt;b&gt;(A) 함수 외부 let 사용 예제 (잘못된 접근)&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;let globalCounter = 0; // 모든 컴포넌트 인스턴스가 공유

function Counter() {
  const handleClick = () =&amp;gt; {
    globalCounter += 1;
    console.log(`글로벌 카운터 값: ${globalCounter}`);
  };

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;button onClick={handleClick}&amp;gt;+1 증가&amp;lt;/button&amp;gt;
      &amp;lt;p&amp;gt;카운터: {globalCounter}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

// &amp;lt;Counter /&amp;gt;를 여러 번 렌더링하면 모두 같은 globalCounter를 공유하게 됨
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;❌ &lt;b&gt;문제점&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 &amp;lt;Counter /&amp;gt; 인스턴스가 &lt;b&gt;globalCounter&lt;/b&gt;라는 &lt;b&gt;하나의 값&lt;/b&gt;을 공유.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;독립적인 상태 관리 불가&lt;/b&gt; &amp;rarr; &lt;b&gt;컴포넌트 재사용성 저하&lt;/b&gt; 및 &lt;b&gt;예상치 못한 동작&lt;/b&gt; 발생.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;✅ &lt;b&gt;(B) useRef 사용 예제 (권장 방법)&lt;/b&gt;&lt;/h4&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import React, { useRef } from &quot;react&quot;;

function Counter() {
  const counterRef = useRef(0); // 각 인스턴스마다 독립적인 값 유지

  const handleClick = () =&amp;gt; {
    counterRef.current += 1;
    console.log(`카운터 값: ${counterRef.current}`);
  };

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;button onClick={handleClick}&amp;gt;+1 증가&amp;lt;/button&amp;gt;
      &amp;lt;p&amp;gt;카운터: {counterRef.current}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

// 각 &amp;lt;Counter /&amp;gt; 인스턴스는 자신의 counterRef 값을 독립적으로 유지
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;결과&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 &amp;lt;Counter /&amp;gt; 컴포넌트는 &lt;b&gt;고유한 counterRef 값을 유지&lt;/b&gt;합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;컴포넌트 재사용성&lt;/b&gt;과 &lt;b&gt;독립적인 상태 관리&lt;/b&gt;가 보장됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;4. 왜 useRef가 더 나은 선택일까?&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  &lt;b&gt;1) 컴포넌트 독립성 보장&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;React에서는 동일한 컴포넌트를 여러 번 사용하더라도 &lt;b&gt;각 인스턴스가 독립적인 상태&lt;/b&gt;를 유지해야 합니다.&lt;/li&gt;
&lt;li&gt;함수 외부에 let을 선언하면 &lt;b&gt;모든 인스턴스가 동일한 값&lt;/b&gt;을 참조하여 &lt;b&gt;의도치 않은 사이드 이펙트&lt;/b&gt;가 발생합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  &lt;b&gt;2) 상태 간섭 방지&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;useRef는 &lt;b&gt;각 컴포넌트 인스턴스마다 고유한 참조값&lt;/b&gt;을 제공하여, &lt;b&gt;다른 컴포넌트의 상태 변경이 영향을 미치지 않도록 방지&lt;/b&gt;합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;  &lt;b&gt;3) React 철학과 일치&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;React는 &lt;b&gt;선언적 프로그래밍&lt;/b&gt;과 &lt;b&gt;상태 격리&lt;/b&gt;를 중시합니다.&lt;/li&gt;
&lt;li&gt;useRef는 이러한 철학에 맞게 &lt;b&gt;안전하고 예측 가능한 상태 관리&lt;/b&gt;를 제공합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;5. 실제 사용 사례: useRef가 필요한 상황&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 100px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt; 상황 &lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt; useRef 사용 이유 &lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;  &lt;b&gt;DOM 참조&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;ref={myRef}로 &lt;b&gt;DOM 요소 직접 참조&lt;/b&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;  &lt;b&gt;렌더링 간 데이터 유지&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;리렌더링 없이&lt;/b&gt; 값을 유지 (예: 타이머 ID).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;⚡ &lt;b&gt;비동기 최신 상태 참조&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;비동기 작업 중 &lt;b&gt;항상 최신 상태 값&lt;/b&gt; 참조.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;  &lt;b&gt;렌더링 최적화&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;불필요한 렌더링 방지&lt;/b&gt; (렌더링 없이 값 업데이트).&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;6. 핵심 요약&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;✅ &lt;b&gt;함수 외부의 let&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 인스턴스가 &lt;b&gt;동일한 값&lt;/b&gt;을 참조하는 &lt;b&gt;글로벌 변수&lt;/b&gt; 역할.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;컴포넌트 독립성 부족&lt;/b&gt;, &lt;b&gt;예상치 못한 사이드 이펙트 발생&lt;/b&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;⚡ &lt;b&gt;useRef&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;각 컴포넌트 인스턴스별 고유 참조값&lt;/b&gt; 유지.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;렌더링 간 값 유지&lt;/b&gt;, &lt;b&gt;렌더링 트리거 없음&lt;/b&gt;, &lt;b&gt;React 철학 준수&lt;/b&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  &lt;b&gt;7. 최종 결론: useRef와 let의 본질적 차이&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &quot;단순히 리렌더링 시 값을 유지하는 것 이상의 기능을 제공하는 것이 바로 useRef입니다. useRef는 &lt;b&gt;컴포넌트의 독립성 보장&lt;/b&gt;, &lt;b&gt;렌더링 최적화&lt;/b&gt;, &lt;b&gt;비동기 상태 관리&lt;/b&gt;, &lt;b&gt;DOM 참조&lt;/b&gt; 등 &lt;b&gt;React의 핵심 철학과 일치하는 안전한 데이터 관리 수단&lt;/b&gt;입니다.&quot;  ✨&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>React</category>
      <category>js</category>
      <category>React</category>
      <category>ref</category>
      <category>useRef</category>
      <author>HanSol_Lim</author>
      <guid isPermaLink="true">https://hansol0607.tistory.com/72</guid>
      <comments>https://hansol0607.tistory.com/72#entry72comment</comments>
      <pubDate>Thu, 20 Feb 2025 17:05:09 +0900</pubDate>
    </item>
  </channel>
</rss>