<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>절대 오류를 확인해!</title>
    <link>https://whereiserror.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Sun, 26 Jul 2026 23:06:28 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>오류확인자</managingEditor>
    <image>
      <title>절대 오류를 확인해!</title>
      <url>https://tistory1.daumcdn.net/tistory/6190728/attach/b73acc5b80df4687b7c7920d9fa7b01c</url>
      <link>https://whereiserror.tistory.com</link>
    </image>
    <item>
      <title>[딥다이브] 4. 변수</title>
      <link>https://whereiserror.tistory.com/298</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 변수(Variable)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수(Variable): 하나의 값을 저장하기 위해 확보한 메모리 공간 자체 또는 그 메모리 공간을 식별하기 위해 붙인 이름을 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말하자면, 값의 위치를 가리키는 상징적인 이름&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;// 변수는 하나의 값을 저장하기 위한 수단
var userId = 1;
var userName = 'Lee';
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;qml&quot;&gt;&lt;code&gt;// 객체 또는 배열 같은 자료구조를 사용하면 여러 개의 값을 하나로 그룹화해서 하나의 값처럼 사용할 수 있다.
var user = { id: 1, name: 'Lee' };

var users = [
  { id: 1, name: 'Lee' },
  { id: 2, name: 'Kim' },
];
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;result: 변수 이름(변수명)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수에 저장된 갑: 변수 값&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수에 값을 저장하는 것을 할당(assignment 대입, 저장)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수에 저장된 값을 읽어 들이는 것을 참조(reference)&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;var result = 10 + 20;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 식별자(identifier)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수 이름을 식별자라고도 한다. 식별자는 어떤 값을 구별해서 식별할 수 있는 고유한 이름을 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;식별자는 어떤 값이 저장되어 있는 메모리 주소를 기억(저장)해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;식별자는 값이 아니라 메모리 주소를 기억하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 변수 선언(Variable declaration)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수 선언이란 변수를 생성하는 것을 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값을 저장하기 위한 메모리 공간을 확보하고, 변수 이름과 확보된 메모리 공간의 주소를 연결해서 값을 저장할 수 있게 준비하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수 선언에 의해 확보된 메모리 공간은 확보가 해제(release)되기 전까지는 누구도 확보된 메모리 공간을 사용할 수 없도록 보호되므로 안전하게 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수를 사용하려면 반드시 선언이 필요하다. 변수를 선언할 때는 var, let, const 키워드를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;var 키워드는 블록 레벨 스코프가 아닌 함수 레벨 스코프를 지원하므로 몇몇의 단점이 있으며 전역 변수가 선언되어 부작용을 발생시키기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 변수 선언을 하고, 변수에 값을 할당하지 않으면 자바스크립트 엔진에 의해 undefined라는 값이 암묵적으로 할당되어 초기화된다.&lt;/p&gt;
&lt;pre class=&quot;gml&quot;&gt;&lt;code&gt;var score; // 변수 선언을 했으나, 값을 할당하지 않은 상태. 암묵적으로 undefined가 할당되어 초기화
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수 이름은 어디에 등록되는가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수 이름을 비롯한 모든 식별자는 실행 컨텍스트에 등록된다. 실행 컨텍스트는 자바스크립트 엔진이 소스코드를 평가하고 실행하기 위해 필요한 환경을 제공하고, 코드의 실행 결과를 실제로 관리하는 영역이다. 자바스크립트 엔진은 실행 컨텍스트를 통해 식별자와 스코프를 관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수 이름과 변수 값은 실행 컨텍스트 내에 키(key) / 값(value) 형식인 객체로 등록되어 관리된다.&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;초기화(initialization)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수가 선언된 이후 최초로 값을 할당하는 것을 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 초기화 단계를 거치지 않으면 확보된 메모리 공간에는 이전에 다른 애플리케이션이 사용했던 값이 남아있을 수 있다. 이러한 값을 쓰레기 값(garbage value)라고 한다.&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;변수를 사용하려면 반드시 선언이 필요하다. 변수 뿐만 아니라 모든 식별자(함수, 클래스 등)가 그렇다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 선언하지 않은 식별자에 접근 하면 ReferenceError(참조 에러)가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;식별자를 통해 값을 참조하려 했지만, 자바스크립트 엔진이 등록된 식별자를 찾을 없을 때 발생하는 에러이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4. 변수 선언의 실행 시점과 변수 호이스팅&lt;/h4&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;console.log(score); // undefined

var score; // 변수 선언문
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수 선언문보다 변수를 참조하는 코드가 앞에 있다. 자바스크립트 코드는 인터프리터에 의해 한 줄씩 순차적으로 실행되므로 console.log(score)가 실행되고, 그리고 다음 줄의 코드가 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 console.log(score)가 실행되는 시점에 아직 score 변수의 선언이 실행되지 않았으므로 참조 에러가 발생한 것 처럼 보인다. 하지만 참조 에러가 발생하지 않고 undefined가 출력된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 변수 선언이 소스코드가 한 줄씩 순차적으로 실행되는 시점, 즉 런타임(runtime)이 아니라 그 이전 단계에서 먼저 실행되기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트 엔진은 소스코드를 한 줄씩 순차적으로 실행하기에 앞서 먼저 소스코드의 평가 과정을 거치면서 소스코드를 실행하기 위한 준비를 한다. 이때 소스코드 실행을 위한 준비 단계인 소스코드의 평가 과정에서 자바스크립트 엔진은 변수 선언을 포함한 모든 선언문(변수 선언문, 함수 선언문 등)을 소스코드에서 찾아내 먼저 실행한다. 그리고 소스코드의 평가 과정이 끝나면 비로소 변수 선언을 포함한 모든 선언문을 제외하고 소스코드를 한 줄씩 순차적으로 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 자바스크립트 엔진은 변수 선언이 소스코드의 어디에 있든 상관없이 다른 코드보다 먼저 실행한다. 따라서 변수 선언이 소스코드의 어디에 위치하는지와 상관없이 어디서든지 변수를 참조할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 변수 선언문인 var score; 보다 변수를 참조하는 코드인 console.log(score)가 앞에 있으므로, 코드를 순차적으로 실행되는 런타임에 변수 선언이 실행되면 console.log(score)가 실행되는 시점에 참조 에러가 발생해야한다. 하지만 undefined가 출력된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 변수 선언(선언 단계와 초기화 단계)이 소스코드가 순차적으로 실행되는 런타임 이전 단계에 먼저 실행된다는 것이다. 이처럼 변수 선언문이 코드의 선두로 끌어 올려진 것처럼 동작하는 자바스크립트 고유의 특징은 변수 호이스팅(variable hoisting)이라 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5. 값의 할당&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수에 값을 할당(대입, 저장)할 때는 할당 연산자 =를 사용한다. 할당 연산자는 우변의 값을 좌변의 변수에 할당한다.&lt;/p&gt;
&lt;pre class=&quot;gml&quot;&gt;&lt;code&gt;var score; // 변수 선언
score = 80; // 값의 할당
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수 선언과 값의 할당을 다음과 같이 하나의 문으로 단축 표현할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;var score = 80;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수 선언은 소스코드가 순차적으로 실행되는 시점인 런타임 이전에 먼저 실행되지만, 값의 할당은 소스코드가 순차적으로 실행되는 시점인 런타임에 실행된다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;console.log(score); // undefined

var score; // 변수 선언
score = 80; // 값의 할당

console.log(score); // 80
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수에 값을 할당 때는 이전 값 undefined가 저장되어 있던 메모리 공간을 지우고 그 메모리 공간에 할당 값 80을 새롭게 저장하는 것이 아니라, 새로운 메모리 공간을 확보하고 그 곳에 할당 값 80을 저장한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;6. 값의 재할당&lt;/h4&gt;
&lt;pre id=&quot;code_1760956570739&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var score = 80; // 변수 선언과 값의 할당
score = 90; // 값의 재할당&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;var 키워드로 선언한 변수는 값을 재할당할 수 있다. 재할당은 현재 변수에 저장된 값을 버리고 새로운 값을 저장하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;var 키워드로 선언한 변수는 선언과 동시에 undefined로 초기화되기 때문에 엄밀히 말하자면 변수에 처음으로 값을 할당하는 것도 사실은 재할당이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재할당은 변수에 저장된 값을 다른 값으로 변경한다. 그래서 변수라고 하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 값을 재할당할 수 없어서 변수에 저장된 값을 변경할 수 없다면 변수가 아니라 상수(constant)라 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상수는 한 번 정해지면 변하지 않는 값이다. 다시 말해 상수는 단 한 번만 할당할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수에 값에 재할당하게 되면 기존 메모리 공간에 저장되어 있던 곳에 새로운 값을 저장하는 것이 아닌, 새로운 메모리 공간을 확보하고 그 공간에 저장하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 저장된 값을 필요 없는 값이므로, 가비지 콜렉터에 의해 메모리에서 자동 해제가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가비지 콜렉터란?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플리케이션이 할당한 메모리 공간을 주기적으로 검사하여 더 이상 사용하지 않는 메모리를 해체하는 기능을 말한다. 더 이상 사용되지 않는 메모리란 간단하게 말하자면, 어떤 식별자도 참조하지 않는 메모리 공간을 의미한다. 자바스크립트는 가비지 콜렉터를 내장하고 있는 매니저드 언어로서 가비지 콜렉터를 통해 메모리 누수를 방지한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;7. 식별자&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;/li&gt;
&lt;li&gt;예약어는 식별자로 사용할 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예약어란?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그래밍 언어에서 사용되고 있거나 사용될 예정인 단어를 말한다. 자바스크립트의 예약어는 다음과 같다&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;// 기본 예약어
break, case, catch, class, const, continue, debugger, default, delete,
do, else, export, extends, finally, for, function, if, import, in,
instanceof, new, return, super, switch, this, throw, try, typeof,
var, void, while, with, yield

// 미래 예약어
enum, implements, interface, let, package, private, protected, public, static

// 엄격 모드 예약어
implements, interface, let, package, private, protected, public, static, yield

// 리터럴 예약어
null, true, false

// 기타 예약어(환경에 따른)
await, arguments, eval, Infinity, NaN, undefined
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트는 대소문자를 구별하므로, 다음과 같은 변수는 각각의 별개의 변수이다.&lt;/p&gt;
&lt;pre class=&quot;abnf&quot;&gt;&lt;code&gt;var firstName;
var firstname;
var FIRSTNAME;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이밍을 할 때, 목적에 맞게 잘 설정해야 한다. 변수 존재의 목적을 분명해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주로 많이 쓰는 것이 카멜케이스와 파스칼 케이스이다.&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;// 카멜 케이스(camelCase)
var firstName;

// 스네이크 케이스(snake_Case)
var first_name;

// 파스칼 케이스(PascalCase)
var FirstName;

// 헝가리언 케이스(typeHungarianCase)
var strFirstName; // type + identifier
var $ele = document.getElementById('myId'); // DOM 노드
var observale$ = fromEvent(document, 'click'); // RxJS 옵저버블
&lt;/code&gt;&lt;/pre&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;var로 선언된 키워드는 런타임 이전에, 소스코드 평가 과정에서 실행이 된다. 이때 변수 선언문, 함수 선언문 모두 실행이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이후 자바스크립트 엔진에 의해 소스코드가 순차적으로 한 줄씩 실행된다. 그래서 만약 값의 할당이 일어나기도 전에 참조하는 코드가 먼저 쓰여져 있다면, var의 경우 변수 선언과 동시에 undefined로 초기화되기 때문에 참조에러가 아닌 undefined로 출력이 된다.&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;여기서 궁금했던 게, 런타임의 정확한 의미가 무엇인가 라고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에서 코드가 메모리에 올라가 실행 컨텍스트에서 실제로 평가되고 동작하는 전체적인 과정을 런타임이라고 하는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드가 자바스크립트 엔진에 의해 해석되어 실행되는 과정이라고 한다.&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;단순하게 한 줄씩 실행하는 것이 아닌, 여러가지 단계가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 파싱(Parsing)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소스코드를 읽고 구문 트리로 변환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문법 오류를 이때 잡는다.&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;2. 컴파일(Compilation)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JIT(Just-In-Time) 컴파일러가 AST를 바이트코드(Bytecode)나 머신코드로 변환&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트는 인터프리터 언어로 보이지만, 실제로는 JIT 컴파일을 사용하여 최적화된 실행을 한다.&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;3. 실행(Runtime Execution)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변환된 코드가 실행 컨텍스트(Exection Context)내에서 실행&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 시점이 우리가 일반적으로 얘기하는 런타임이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 변수 호이스팅, 스코프 체인, this 바인딩, 이벤트 루프 등이 작동한다.&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;p data-ke-size=&quot;size16&quot;&gt;런타임 이전에 소스코드 평가과정에서 변수 선언, 함수 선언 등을 실행한다고 하는데 실행 컨텍스트 전에 실행이 되는지, 아니면 그냥 포함이 된건지 궁금했다.&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;실행 컨텍스트는 생성과 실행 두 단계로 나뉘는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트 코드는 실행 컨텍스트 안에서 동작을 한다. 이는 두 단계인데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 생성 단계(Creation Phase)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드가 실제로 실행되기 전에 발생&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소스코드 평가 과정이 이 단계에서 일어남&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트 엔진은 이때 변수 선언(var), 함수 선언문, this, 스코프 체인 등을 먼저 설정함&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;2. 실행 단계(Execution Phase)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 한 줄씩 코드를 실제로 실행함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수에 값이 대입되고, 함수가 호출되고, 제어문이 동작하는 시점이다.&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;즉, 소스코드 평가 과정 또한 실행 컨텍스트의 일부이다. 소스코드 평가 과정은 실행 컨텍스트의 두 단계 중 초기 단계인 생성 단계에서 일어나는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;var의 호이스팅은 생성 단계에서 미리 처리하는 로직이다.&lt;/p&gt;</description>
      <category>JavaScript</category>
      <category>JS</category>
      <author>오류확인자</author>
      <guid isPermaLink="true">https://whereiserror.tistory.com/298</guid>
      <comments>https://whereiserror.tistory.com/298#entry298comment</comments>
      <pubDate>Mon, 20 Oct 2025 19:55:47 +0900</pubDate>
    </item>
    <item>
      <title>JSON, XML</title>
      <link>https://whereiserror.tistory.com/297</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. JSON&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JavaScript의 객체문법으로 구조화된 데이터 교환 방식이다. JavaScript 뿐만 아니라 JAVA, Python에서도 데이터 교환 방식으로 쓰인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSON의&amp;nbsp; 타입은 String, Number, Boolean, Object, Array, Null가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. 직렬화, 역직렬화&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSON에서는 데이터 교환하는 방식이 직렬화, 역직렬화 두가지가 있다.&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;직렬화(Serialization)&lt;/b&gt;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터를 JSON 문자열(string)로 변환하는 기술이다.&lt;/li&gt;
&lt;li&gt;JSON.stringify()&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;역직렬화(Deserialization)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;내부에서 사용할 수 있도록&amp;nbsp; JSON 문자열(string)을 JavaScript의 객체문법으로 변환하는 기술이다.&lt;/li&gt;
&lt;li&gt;JSON.parse()&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSON의 사용되는 사례는 주로, API 혹은 시스템을 구성하는 설정파일에 활용이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) API, Package.json&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. XML&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XML은 마크업 언어 중 하나로, 태그를 이용하여 데이터를 저장, 전송하는 데이터 교환 방식이다.&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;마크업은 태그나 기호 등을 이용하여, 문서나 데이터의 구조를 나타내는 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 마크업 언어는 HTML, XML, Markdown, LaTex 등이 있다.&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;XML의 구성은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 프롤로그: 버젼, 인코딩&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 루트요소(단 하나)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 하위 요소들&lt;/p&gt;
&lt;pre id=&quot;code_1742390983765&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;OSTList&amp;gt;
&amp;lt;OST like=&quot;1&quot;&amp;gt;
&amp;lt;name&amp;gt;마녀 배달부 키키&amp;lt;/name&amp;gt; &amp;lt;song&amp;gt;따스함에 둘러쌓인다면&amp;lt;/song&amp;gt;
&amp;lt;/OST&amp;gt;
&amp;lt;OST like=&quot;2&quot;&amp;gt;
&amp;lt;name&amp;gt;하울의 움직이는 성&amp;lt;/name&amp;gt; &amp;lt;song&amp;gt;세계의 약속&amp;lt;/song&amp;gt;
&amp;lt;/OST&amp;gt;
&amp;lt;/OSTList&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 구성이 되어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. HTML와 XML의 차이&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;HTML&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;HTML은 주로 데이터를 표시하는, UI를 보여주며&lt;/li&gt;
&lt;li&gt;HTML은 정해진 태그를 사용한다.&lt;/li&gt;
&lt;li&gt;대소문자 구분이 없다.&lt;/li&gt;
&lt;li&gt;종료태그가 필요없는 태그도 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;XML&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;/li&gt;
&lt;li&gt;대소문자 구분이 있다.&lt;/li&gt;
&lt;li&gt;종료태그가 필수이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;5. Sitemap.xml&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sitemap.xml은 xml을 사용할 때 필수적으로 사용하는데, 이 이유는 큰 사이트거나, 링크가 서로 종속적으로 연결되어 있지 않을 경우, 페이지의 데이터 누락이 생길 수도 있다. 이를 방지하기 위하여 sitemap.xml을 사용한다.&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;sitemap.xml이란&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 내의 모든 페이지들을 리스트업한 데이터라고 보면 된다. 자세하게 말하자면 웹 사이트의 모든 페이지 목록을 포함한 XML 파일로, 검색 엔진이 사이트의 콘텐츠를 효과적으로 크롤링(수집)할 수 있도록 도와준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대규모 사이트나 링크가 서로 종속적으로 연결되지 않은 사이트에서는 필수적으로 사용된다.&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;위와 같이 sitemap을 사용하는 이유는&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;검색 엔진 크롤링 최적화
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;검색 엔진(google, Bing 등)이 사이트의 모든 페이지를 빠짐없이 인식하도록 도와준다.&lt;/li&gt;
&lt;li&gt;내부 링크가 부족하거나, 페이지 간 연결이 약한 경우에도 사이트의 구조를 명확하게 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SEO(검색 엔진 최적화)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사이트의 모든 페이지가 검색 엔진에 색인(indexing)될 가능성을 높인다.&lt;/li&gt;
&lt;li&gt;웹 사이트 내 중요한 페이지를 우선적으로 검색엔진에 노출&lt;/li&gt;
&lt;li&gt;페이지의 최신 업데이트 정보를 제공하여 검색 엔진이 더 빠르게 반영될 수 있도록 도와준다.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;6. JSON과 XML의 비교&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSON과 XML은 둘다 데이터 전송 방식이다. 하지만 현 시점에서는 XML보다 JSON을 대중적으로 사용하는데, 그 이유는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XML의 경우는&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;반면 JSON의 경우는 키 - 값(key - value)의 구조로 데이터가 좀 더 간결하다.&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;그리고 JSON의 경우 JS 객체 이기 때문에, 웹 브라우저나 서버에서 바로 객체로 변환하여 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XML의 경우는 데이터를 사용할 경우 추가적인 파싱이 필요하여 속도가 느릴 수도 있다.&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;또한, REST API의 경우는 기본적으로 JSON을 사용 하며, 브라우저 및 다양한 프로그래밍 언어에서는 JSON의 지원이 아주 강력하다.&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;하지만, XML이 아예 안쓰이는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XML이 유리한 경우도 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문서 기반의 데이터 구조
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;XML은 HTML과 비슷한 계층 구조를 가지고 있기 때문에, 문서 저장에 적합하다.&lt;/li&gt;
&lt;/ul&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;XML은 DTD, XSD와 같은 스키마(Schema)를 이용하여 데이터를 엄격하게 정의가 가능&lt;/li&gt;
&lt;li&gt;금융, 의료, 법률 시스템에서 데이터 검증이 중요한 경우에 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SOAP API에서 사용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SOAP(Simple Object Access Protocol) 기반 웹 서비스에서는 XML이 필수적으로 사용됨&lt;/li&gt;
&lt;li&gt;금융권, 공공기관 등 보안이 중요한 환경에서는 XML 기반 SOAP API를 여전히 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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;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;</description>
      <category>CS</category>
      <author>오류확인자</author>
      <guid isPermaLink="true">https://whereiserror.tistory.com/297</guid>
      <comments>https://whereiserror.tistory.com/297#entry297comment</comments>
      <pubDate>Wed, 19 Mar 2025 22:47:14 +0900</pubDate>
    </item>
    <item>
      <title>XSS 보안</title>
      <link>https://whereiserror.tistory.com/294</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트를 진행 하던 중 textarea를 사용하고, 여기 사용자가 입력한 데이터를 서버로 전송 그리고 이를 받아 출력하는 과정에서 문제가 생겼다. 그 문제는 이제 사용자가 입력한 내용에 태그까지 포함이 되는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;179&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/B1WdV/btsLClFjQsv/Mo3M0KIBPTM4wpMIS0Out0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/B1WdV/btsLClFjQsv/Mo3M0KIBPTM4wpMIS0Out0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/B1WdV/btsLClFjQsv/Mo3M0KIBPTM4wpMIS0Out0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FB1WdV%2FbtsLClFjQsv%2FMo3M0KIBPTM4wpMIS0Out0%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;752&quot; height=&quot;179&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;179&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 내용에 태그까지 같이 포함된 것이다. 이는 내용 읽는데 문제 있을 뿐만 아니라 보안상의 문제가 생길수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 입력값에 태그까지 포함된 이유가 있다. {} 내부의 값이 HTML 코드가 포함된 문자열인 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML 태그를 인코딩해서 처리하므로, 브라우저에는 태그가 그대로 보이게 된다. 이러한 이유로 태그가 보이는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 여기에 문제가 태그가 그대로 보인다면 XSS 공격에 취약해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. XSS란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 해킹 공격 중 XSS라는 공격 기법이 있다. 이는 Cross Site Scripting의 약자로, XSS는 게시판이나, 웹 메일 등에 자바 스크립트와 같은 스크립트 코드를 삽입 하여 개발자가 고려하지 않은 기능이 작동하게 하는 치명적인 공격이 될 수 있다. 이는 대부분의 웹 해킹 공격 방식이 아닌 클라이언트 즉, 사용자를 대상으로 한 공격이다.&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;p data-ke-size=&quot;size16&quot;&gt;XSS의 공격 기법은 크게 Reflected XSS, Stored XSS, 그리고 DOM Based XSS 세가지가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Reflected XSS&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 ID를 입력하면 Welcome 000 를 출력해주는 페이지가 있다고 가정하자,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 페이지에 간단한 스크립트를 삽입하면 별다른 필터링 없이 실행이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공격자는 이러한 취약점을 존재하는 페이지를 미리 탐색한 후 XSS 공격을 위한 스크립트가 포함 된 URL을 공격 대상자에게 노출시키는 방법으로 공격할 수 있다.&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;Reflected XSS의 공격 순서는 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;294&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bh1J8S/btsLDJZjx9b/kObKoFKuomCJ4uiVcGlcZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bh1J8S/btsLDJZjx9b/kObKoFKuomCJ4uiVcGlcZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bh1J8S/btsLDJZjx9b/kObKoFKuomCJ4uiVcGlcZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbh1J8S%2FbtsLDJZjx9b%2FkObKoFKuomCJ4uiVcGlcZk%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;752&quot; height=&quot;294&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;294&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;사용자가 해당 URL을 클릭할 경우, 취약한 웹 사이트의 서버에 스크립트가 포함 된 URL을 통해 Request를 전송하고, 웹 서버에서는 해당 스크립트를 포함한 Response를 전송하게 된다. 이 과정에서 브라우저에서 실행되면 보안 취약점이 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Stored XSS&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stored XSS는 Reflected XSS와는 달리 웹 사이트의 게시판에 스크립트 삽입하는 공격 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공격자는 게시판에 스크립트를 삽입한 후 공격 대상자가 해당 게시물을 클릭하도록 유도한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-01-03 오후 7.59.17.png&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cii8p1/btsLDunMG1z/mkpKeOK2rP2q13quNWaXzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cii8p1/btsLDunMG1z/mkpKeOK2rP2q13quNWaXzK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cii8p1/btsLDunMG1z/mkpKeOK2rP2q13quNWaXzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcii8p1%2FbtsLDunMG1z%2FmkpKeOK2rP2q13quNWaXzK%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;752&quot; height=&quot;306&quot; data-filename=&quot;스크린샷 2025-01-03 오후 7.59.17.png&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;306&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공격자가 미리 XSS 공격에 취약한 웹사이트를 탐색하고 XSS 공격을 위한 스크립트를 포함한 게시글을 웹 사이트에 업로드 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게시글의 URL을 사용자에게 노출하고, 사용자가 게시글이 확임함으로써 URL에 대한 요청을 서버에 전송하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 서버에서 스크립트를 포함한 Response를 전송하며 공격이 수행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. DOM Based XSS&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DOM Based XSS는 웹 애플리케이션의 클라이언트 측 코드(JS)에서 DOM을 직접 조작하는 과정에서 악성 스크립트가 실행되는 취약점을 말한다. 서버와의 통신&amp;nbsp; 없이 클라이언트 측에서만 발생하기 때문에 탐지하기 어려운 특징이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;URL, 프래그먼트(#fragment), DOM 조작 등을 통해 입력 값이 처리된다.&lt;/li&gt;
&lt;li&gt;서버는 문제없이 동작하지만, 브라우저에서 악성 스크립트가 실행된다.&lt;/li&gt;
&lt;/ul&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; 방법이 있지만 내가 해결한 방법은 dangerouslySetInnerHTML 속성을 사용하여 HTML 태그를 인코딩하지 않게 한다. 하지만 이는 완벽하지 않다. 이 이유는 문자열을 HTML로 렌더링 할 수 있게하여, 외부 데이터가 포함된 경우 XSS 공격에 매우 취약하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;악성 코드가 포함된 HTML이나 스크립트를 실행할 위험이 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 여기에 추가 시킨 것이. DOMPurify라는 라이브러리를 다운 받아 사용하는 것이다. 이는 리액트에서dangerouslySetInnerHTML&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 사용할 때, 외부 데이터가 포함된 경우 DOMPurify를 통해&amp;nbsp; 데이터를 정제한 후 렌더링함으로써 예방할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1735902574535&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import DOMPurify from &quot;dompurify&quot;;
  const sanitizedContent = DOMPurify.sanitize(`${data?.item.content}`);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 import를 하고&amp;nbsp; npm install dompurify 설치 해야한다. 그리고 사용하는 곳에&lt;/p&gt;
&lt;pre id=&quot;code_1735902615368&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; &amp;lt;span dangerouslySetInnerHTML={{__html: sanitizedContent,}}&amp;gt;&amp;lt;/span&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 실행하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 아래와 같이 태그를 삭제한 텍스트만 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;152&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdlFwr/btsLDW5eLN7/1FEUZF1vBHbgujF66pSOJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdlFwr/btsLDW5eLN7/1FEUZF1vBHbgujF66pSOJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdlFwr/btsLDW5eLN7/1FEUZF1vBHbgujF66pSOJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdlFwr%2FbtsLDW5eLN7%2F1FEUZF1vBHbgujF66pSOJ1%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;752&quot; height=&quot;152&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;152&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>React/다시 공부하는 리액트</category>
      <author>오류확인자</author>
      <guid isPermaLink="true">https://whereiserror.tistory.com/294</guid>
      <comments>https://whereiserror.tistory.com/294#entry294comment</comments>
      <pubDate>Fri, 3 Jan 2025 20:13:32 +0900</pubDate>
    </item>
    <item>
      <title>useMatch</title>
      <link>https://whereiserror.tistory.com/293</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. 원하는 기능&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;350&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chfF7G/btsLA5agfNk/nk1dh9nQCfEQeIy4A6CfEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chfF7G/btsLA5agfNk/nk1dh9nQCfEQeIy4A6CfEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chfF7G/btsLA5agfNk/nk1dh9nQCfEQeIy4A6CfEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchfF7G%2FbtsLA5agfNk%2Fnk1dh9nQCfEQeIy4A6CfEK%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;1280&quot; height=&quot;350&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;350&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;br /&gt;위와&amp;nbsp;같이&amp;nbsp;조건에&amp;nbsp;따라&amp;nbsp;각자&amp;nbsp;다른&amp;nbsp;페이지에서&amp;nbsp;다른&amp;nbsp;헤더를&amp;nbsp;보여주게&amp;nbsp;합니다.&lt;br /&gt;&lt;br /&gt;2. 사용한 기술&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useMatch&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;&lt;br /&gt;필요한&amp;nbsp;패스를&amp;nbsp;변수로&amp;nbsp;선언한다.&lt;/p&gt;
&lt;pre id=&quot;code_1735462091435&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; const prId = useMatch(&quot;pr/:_id&quot;);
 const prIdEdit = useMatch(&quot;pr/:_id/edit&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;그리고&amp;nbsp;하나의&amp;nbsp;변수로&amp;nbsp;이를&amp;nbsp;묶는다.&lt;/p&gt;
&lt;pre id=&quot;code_1735462098848&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const headerMatch =
    prId ||
    prIdEdit ||&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;패스를&amp;nbsp;할당&amp;nbsp;받은&amp;nbsp;변수를&amp;nbsp;if문으로&amp;nbsp;조건을&amp;nbsp;준다.&lt;/p&gt;
&lt;pre id=&quot;code_1735462105892&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  if (headerMatch) {
    return (
      &amp;lt;header className=&quot;relative w-full h-[60px] flex items-center justify-center border-b border-gray-200 mb-5&quot;&amp;gt;
        &amp;lt;img
          src=&quot;/icons/back.svg&quot;
          className=&quot;absolute left-0 w-6 h-6 hover:cursor-pointer&quot;
        /&amp;gt;
        &amp;lt;div className=&quot;w-full text-center text-[24px] font-bold&quot;&amp;gt;제목&amp;lt;/div&amp;gt;
      &amp;lt;/header&amp;gt;
    );
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;그러면&amp;nbsp;저&amp;nbsp;조건에&amp;nbsp;맞은&amp;nbsp;페이지는&amp;nbsp;위의&amp;nbsp;헤더를&amp;nbsp;보여줄&amp;nbsp;것이고,&amp;nbsp;그게&amp;nbsp;아니라면&amp;nbsp;다른&amp;nbsp;헤더를&amp;nbsp;보여준다.&lt;br /&gt;&lt;br /&gt;해결&amp;nbsp;방법&lt;br /&gt;1.&amp;nbsp;useMatch(패스)를&amp;nbsp;사용한다.&lt;br /&gt;&amp;nbsp;&amp;nbsp;const&amp;nbsp;prId&amp;nbsp;=&amp;nbsp;useMatch(&quot;pr/:_id&quot;);&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;2.&amp;nbsp;이&amp;nbsp;변수를&amp;nbsp;묶는&amp;nbsp;또&amp;nbsp;다른&amp;nbsp;변수를&amp;nbsp;생성&lt;br /&gt;&amp;nbsp;&amp;nbsp;const&amp;nbsp;headerMatch&amp;nbsp;=&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;prId&amp;nbsp;||&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;3.&amp;nbsp;if문&amp;nbsp;혹은&amp;nbsp;다른&amp;nbsp;조건문을&amp;nbsp;이용하여&amp;nbsp;조건을&amp;nbsp;설정&lt;br /&gt;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(headerMatch)&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;(&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;위와&amp;nbsp;같이&amp;nbsp;조건부&amp;nbsp;렌더링이&amp;nbsp;필요한&amp;nbsp;경로를&amp;nbsp;변수로&amp;nbsp;설정하고,&amp;nbsp;그&amp;nbsp;변수를&amp;nbsp;이용하여&amp;nbsp;조건부&amp;nbsp;렌더링을&amp;nbsp;할&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp;이&amp;nbsp;방식을&amp;nbsp;이용하면&amp;nbsp;헤더뿐만&amp;nbsp;아니라,&amp;nbsp;Footer나&amp;nbsp;다른&amp;nbsp;페이지별로&amp;nbsp;다르게&amp;nbsp;보여야&amp;nbsp;하는&amp;nbsp;컴포넌트에도&amp;nbsp;동일한&amp;nbsp;조건부&amp;nbsp;렌더링&amp;nbsp;로직을&amp;nbsp;적용할&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp;또한,&amp;nbsp;이러한&amp;nbsp;경로&amp;nbsp;매칭&amp;nbsp;로직을&amp;nbsp;유틸리티&amp;nbsp;함수로&amp;nbsp;분리하면&amp;nbsp;재사용성과&amp;nbsp;유지보수성이&amp;nbsp;더욱&amp;nbsp;높아진다.&lt;br /&gt;&lt;br /&gt;이&amp;nbsp;방법&amp;nbsp;외에도&amp;nbsp;useLocation과&amp;nbsp;startsWith를&amp;nbsp;사용하여&amp;nbsp;경로&amp;nbsp;접두사를&amp;nbsp;기준으로&amp;nbsp;조건부&amp;nbsp;렌더링을&amp;nbsp;처리할&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp;다만,&amp;nbsp;startsWith는&amp;nbsp;고정된&amp;nbsp;문자열만&amp;nbsp;처리할&amp;nbsp;수&amp;nbsp;있기&amp;nbsp;때문에,&amp;nbsp;동적&amp;nbsp;경로나&amp;nbsp;숫자가&amp;nbsp;포함된&amp;nbsp;경로(/pr/123&amp;nbsp;등)는&amp;nbsp;제대로&amp;nbsp;처리하지&amp;nbsp;못할&amp;nbsp;가능성이&amp;nbsp;있다.&amp;nbsp;그래서&amp;nbsp;이&amp;nbsp;방법을&amp;nbsp;보완할&amp;nbsp;수&amp;nbsp;있는게,&amp;nbsp;정규&amp;nbsp;표현식을&amp;nbsp;사용하여,&amp;nbsp;위와&amp;nbsp;같은&amp;nbsp;/pr/123&amp;nbsp;같은&amp;nbsp;숫자가&amp;nbsp;포함된&amp;nbsp;경로를&amp;nbsp;처리하는&amp;nbsp;것이다.&amp;nbsp;정규표현식을&amp;nbsp;사용하는&amp;nbsp;방법도&amp;nbsp;있다.&amp;nbsp;이&amp;nbsp;방법은&amp;nbsp;복잡해질&amp;nbsp;수&amp;nbsp;있지만,&amp;nbsp;동적&amp;nbsp;경로나&amp;nbsp;다양한&amp;nbsp;패턴을&amp;nbsp;매칭할&amp;nbsp;때&amp;nbsp;매우&amp;nbsp;유용하다.&lt;br /&gt;&lt;br /&gt;프로젝트에&amp;nbsp;따라서&amp;nbsp;정규식과&amp;nbsp;useLocation을&amp;nbsp;조합하거나,&amp;nbsp;React&amp;nbsp;Router의&amp;nbsp;useMatch를&amp;nbsp;활용하는&amp;nbsp;것이&amp;nbsp;더&amp;nbsp;적합할&amp;nbsp;수&amp;nbsp;있다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>React</category>
      <author>오류확인자</author>
      <guid isPermaLink="true">https://whereiserror.tistory.com/293</guid>
      <comments>https://whereiserror.tistory.com/293#entry293comment</comments>
      <pubDate>Sun, 29 Dec 2024 17:48:45 +0900</pubDate>
    </item>
    <item>
      <title>Next.js 란?</title>
      <link>https://whereiserror.tistory.com/292</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Next.js 란&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React의 기능을 확장한 프레임워크로 SSR(서버 사이드 렌더링)과 SSG(정적 사이트 생성) 같은 기능을 기본적으로 제공하며, SEO와 성능 최적화에 강점을 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1-1. 서버 사이드 렌더링(SSR)&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;서버에서 HTML을 생성하며 클라이언트에 전달한다.&lt;/li&gt;
&lt;li&gt;초기 렌더링 속도가 빠르고, SEO에 최적화되어 검색 엔진에 유리하다.&lt;/li&gt;
&lt;li&gt;페이지 요청 시 데이터를 즉시 가져와서 렌더링한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1-2. 정적 사이트 생성(SSG)&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;빌드 시 HTML 파일을 생성하므로 빠른 로드 속도를 제공한다.&lt;/li&gt;
&lt;li&gt;자주 바뀌지 않는 컨텐츠에 적합하며,사용자가 빠르게 접근할 수 있다.&lt;/li&gt;
&lt;li&gt;데이터는 빌드 시점에 고정된다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1-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;pages 디렉터리 안의 파일 이름이 자동으로 URL 경로가 된다.&lt;/li&gt;
&lt;li&gt;별도의 라우터 설정 없이 동적 라우팅과 정적 라우팅이 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;pages/index.js -&amp;gt; /&lt;/li&gt;
&lt;li&gt;pages/about.js -&amp;gt; /about&lt;/li&gt;
&lt;li&gt;pages/blog/[id].js -&amp;gt; /blog/:id&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1-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;/li&gt;
&lt;li&gt;사용자가 인터랙션을 수행한느 SPA(Single Page Application) 기능도 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1-5. API 라우트 지원&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;pages/api 디렉터리에서 간단한 API 엔드포인트를 생성할 수 있다.&lt;/li&gt;
&lt;li&gt;서버리스(Serverless) 함수처럼 작동하며, 별도의 백엔드 설정이 필요없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1-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;next/image 를 사용해 이미지 크기, 포맷 등을 자동으로 최적화한다.&lt;/li&gt;
&lt;li&gt;Lazy Loading을 기본 제공하여 성능이 뛰어나다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Lazy Loading: 중요도가 떨어지거나 당장 화면에 보이지 않는 요소들의 로딩을 우선적으로 시행하지 않으면서 웹페이지 로딩 퍼모선스를 최적하는 기술이다.&lt;/li&gt;
&lt;li&gt;최초 페이지 로딩 시간을 개선하고, 최초 데이터 전달 양을 감소시키는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1-7. CSS와 스타일링 지원&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;CSS Modules: 로컬 스타일 스코핑이 가능&lt;/li&gt;
&lt;li&gt;Styled JSX: JSX 안에서 스타일링 가능&lt;/li&gt;
&lt;li&gt;Tailwind CSS, Sass, Styled-components 등 CSS 라이브러리와 호환&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1-8. TypeScript 지원&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;TypeScript를 기본적으로 지원하여 정적 타입 검사가 가능&lt;/li&gt;
&lt;li&gt;타입 안정성을 높이고 코드 유지 보수가 용이하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1-9. ISR(Incremental Static Regeneration)&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;SSG의 확장 기능으로, 빌드 후 특정 간격마다 페이지를 다시 생성한다.&lt;/li&gt;
&lt;li&gt;실시간 데이터를 정적으로 제공할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1734155062476&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export async function getStaticProps() {
  const res = await fetch('https://api.example.com/data');
  const data = await res.json();

  return {
    props: { data },
    revalidate: 10, // 10초마다 새 데이터를 생성
  };
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Next.js의 장점&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SEO 최적화: SSR/SSG로 검색 엔진 친화적&lt;/li&gt;
&lt;li&gt;빠른 속도: 이미지 최적화, 정적 빌드&lt;/li&gt;
&lt;li&gt;개발 생산성 향상: 파일 기반 라우팅, API 라우트, 스타일링 옵션 다양하다.&lt;/li&gt;
&lt;li&gt;유연성: CSR, SSR, SSG 모두 지원한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. Next.js의 단점&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;/li&gt;
&lt;li&gt;서버리스 API 라우트는 복잡한 백엔드에 적합하지 않을수도 있다.&lt;/li&gt;
&lt;li&gt;빌드 시간이 많은 데이터가 있을 경우 길어진다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Next.js</category>
      <author>오류확인자</author>
      <guid isPermaLink="true">https://whereiserror.tistory.com/292</guid>
      <comments>https://whereiserror.tistory.com/292#entry292comment</comments>
      <pubDate>Sat, 14 Dec 2024 14:47:31 +0900</pubDate>
    </item>
    <item>
      <title>React Zustand persist 미들웨어</title>
      <link>https://whereiserror.tistory.com/291</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. persist 미들웨어&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;persist는 Zustand 상태를 지정된 스토리지에 저장하고, 페이지를 새로고침하거나 애플리케이션을 다시 열 때 해당 상태를 복원하는 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1-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;상태 저장 및 원본: 상태를 브라우저의 localStorage 또는 sessionStorage 같은 스토리지에 저장한다.&lt;/li&gt;
&lt;li&gt;기본 키 제공: 저장소에서 상태를 구분하기 위한 키를 설정할 수 있다.&lt;/li&gt;
&lt;li&gt;직렬화/역직렬화: 상태를 JSON형식으로 저장하고 복원한다.&lt;/li&gt;
&lt;li&gt;선택적 상태 저장: 필요한 특정 상태만 선택적으로 저장할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1733817988608&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { create } from 'zustand';
import { persist } from 'zustand/middleware';

const useStore = create(
  persist(
    (set) =&amp;gt; ({
      count: 0,
      increment: () =&amp;gt; set((state) =&amp;gt; ({ count: state.count + 1 })),
    }),
    {
      name: 'my-store', // localStorage에 저장될 키
    }
  )
);

export default useStore;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;persist는 내부적으로 상태 변화를 감지한다.&lt;/li&gt;
&lt;li&gt;상태가 변경되면 해당 상태를 JSON으로 직렬화하고 설정된 스토리지에 저장한다.&lt;/li&gt;
&lt;li&gt;애플리케이션이 다시 실행되 때 저장된 상태를 역질렬화하고 복원한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. createJSONStorage&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;createJSONStorage는 기본적으로 persist에서 사용하는 저장소를 설정하기 위해 사용된다. 예를 들어, 상태를 localStorage 대신 sessionStorage에 저장하려는 경우, 이 유틸리티를 사ㅓ용해 커스텀 저장소를 생성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2-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; 다양한 스토리지 지원: 기본적으로 localStorage를 사용하지만, 원하는 스토리지(예: sessionStorage 또는 커스텀 스토리지)로 변경할 수 있다.&lt;/li&gt;
&lt;li&gt;JSON 기반 직렬화/역직렬화: 상태를 JSON 형식으로 저장하고 복원한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1733818209741&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';

const useStore = create(
  persist(
    (set) =&amp;gt; ({
      user: null,
      setUser: (user) =&amp;gt; set({ user }),
    }),
    {
      name: 'user-store', // 스토리지 키
      storage: createJSONStorage(() =&amp;gt; sessionStorage), // sessionStorage를 사용
    }
  )
);

export default useStore;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;createJSONStorage를 통해 원하는 스토리지를 설정한다.&lt;/li&gt;
&lt;li&gt;persist는 이 설정을 기반으로 상태를 저장하고 복원한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. 옵션&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;persist는 다양한 옵션을 제공하여 동작 방식을 커스텀마이징을 할 수 있다.&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 style=&quot;text-align: justify;&quot;&gt;&lt;b&gt;옵션&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: justify;&quot;&gt;&lt;b&gt;설명&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;text-align: justify;&quot;&gt;&lt;b&gt;기본값&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;name&lt;/td&gt;
&lt;td&gt;스토리지에 저장될 키 이름&lt;/td&gt;
&lt;td&gt;필수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;storage&lt;/td&gt;
&lt;td&gt;사용할 스토리지 (localStorage, sessionStorage, 또는 커스텀)&lt;/td&gt;
&lt;td&gt;localStorage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;serialize&lt;/td&gt;
&lt;td&gt;상태를 저장하기 전에 커스터마이징하는 함수&lt;/td&gt;
&lt;td&gt;JSON.stringify&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;deserialize&lt;/td&gt;
&lt;td&gt;저장된 데이터를 불러올 때 커스터마이징하는 함수&lt;/td&gt;
&lt;td&gt;JSON.parse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;partialize&lt;/td&gt;
&lt;td&gt;상태에서 특정 부분만 저장하도록 필터링&lt;/td&gt;
&lt;td&gt;저장된 모든 상태&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;onRehydrateStorage&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1733818340972&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;persist(
  (set) =&amp;gt; ({
    count: 0,
    text: '',
  }),
  {
    name: 'partial-store',
    partialize: (state) =&amp;gt; ({ count: state.count }), // count만 저장
  }
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;상태 복원 후 특정 동작 수행&lt;/b&gt;&lt;/p&gt;
&lt;div id=&quot;mttContainer&quot; class=&quot;notranslate&quot; style=&quot;transform: translate(873px, 188px);&quot; aria-expanded=&quot;false&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;pre id=&quot;code_1733818383791&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const useStore = create(
  persist(
    (set) =&amp;gt; ({
      token: null,
      setToken: (token) =&amp;gt; set({ token }),
    }),
    {
      name: 'auth-store',
      onRehydrateStorage: () =&amp;gt; {
        console.log('스토리지에서 상태 복원 완료!');
      },
    }
  )
);&lt;/code&gt;&lt;/pre&gt;</description>
      <category>React/다시 공부하는 리액트</category>
      <author>오류확인자</author>
      <guid isPermaLink="true">https://whereiserror.tistory.com/291</guid>
      <comments>https://whereiserror.tistory.com/291#entry291comment</comments>
      <pubDate>Tue, 10 Dec 2024 17:13:11 +0900</pubDate>
    </item>
    <item>
      <title>React Fetch-on-render, Fetch-then-render, Render-as-you-fetch,</title>
      <link>https://whereiserror.tistory.com/290</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Fetch-on-render&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1-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;/li&gt;
&lt;li&gt;일반적으로 useEffect를 사용하여 데이터 fetching을 실행하고, 상태를 업데이트하여 화면에 데이터를 렌더링한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1-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;구현이 간단하고, React의 기존 생명 주기를 따른다.&lt;/li&gt;
&lt;li&gt;초기에 최소한의 코드로 시작하기에 적합하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1-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;/li&gt;
&lt;li&gt;사용자 경험(UX)이 떨어질 수 있다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1733393107376&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Component() {
  const [data, setData] = React.useState(null);

  React.useEffect(() =&amp;gt; {
    fetch('/api/data')
      .then((response) =&amp;gt; response.json())
      .then((data) =&amp;gt; setData(data));
  }, []);

  if (!data) return &amp;lt;p&amp;gt;Loading...&amp;lt;/p&amp;gt;;

  return &amp;lt;div&amp;gt;{data.title}&amp;lt;/div&amp;gt;;
}&lt;/code&gt;&lt;/pre&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 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Fetch-then-render&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2-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;/li&gt;
&lt;li&gt;컴포넌트가 렌더링 되기 전에 데이터 fetching이 완료되도록 보장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2-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; 사용자가 데이터를 보기 전에 로딩 상태를 처리할 수 있어 더 나은 UX를 제공한다.&lt;/li&gt;
&lt;li&gt;서버 측 렌더링(SSR)과 잘 맞는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2-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;데이터 fetching이 완료될 때까지 렌더링이 지연된다.&lt;/li&gt;
&lt;li&gt;로딩 상태가 길어지면 사용자 경험에 부정적인 영향을 줄 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1733393256619&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async function fetchData() {
  const response = await fetch('/api/data');
  return response.json();
}

function Component({ data }) {
  return &amp;lt;div&amp;gt;{data.title}&amp;lt;/div&amp;gt;;
}

// 데이터 fetching 후 컴포넌트에 전달
fetchData().then((data) =&amp;gt; {
  ReactDOM.render(&amp;lt;Component data={data} /&amp;gt;, document.getElementById('root'));
});&lt;/code&gt;&lt;/pre&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 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. Render-as-you-fetch&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3-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;/li&gt;
&lt;li&gt;Suspense와 함께 사용하여 로딩 상태를 효율적으로 처리한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3-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;/li&gt;
&lt;li&gt;코드 분할 및 비동기 렌더링과 결합하면 매우 효율적이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3-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;/li&gt;
&lt;li&gt;Suspense 및 React.lazy에 대한 이해가 필요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3-4. Suspense를 이용한 구현&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1733393422755&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const dataPromise = fetch('/api/data').then((res) =&amp;gt; res.json());

function fetchData() {
  const data = dataPromise.read(); // Suspense가 데이터를 읽을 수 있도록 처리
  return data;
}

function Component() {
  const data = fetchData();
  return &amp;lt;div&amp;gt;{data.title}&amp;lt;/div&amp;gt;;
}

function App() {
  return (
    &amp;lt;React.Suspense fallback={&amp;lt;p&amp;gt;Loading...&amp;lt;/p&amp;gt;}&amp;gt;
      &amp;lt;Component /&amp;gt;
    &amp;lt;/React.Suspense&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. Suspense란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React의 Suspense는 비동기적으로 데이터를 가져오거나 컴포넌트를 로드하는 동안 UI의 특정 부분을 대체 콘텐츠로 표시하는 기능이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4-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;데이터를 로딩 중일 때 대체 UI(fallback)를 제공&lt;/li&gt;
&lt;li&gt;Raect.lazy와 함께 코드 분할을 쉽게 처리&lt;/li&gt;
&lt;li&gt;서버 컴포넌트와 클라이언트 컴포넌트의 데이터 fetching 통합&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4-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;/li&gt;
&lt;li&gt;복잡한 로딩 상태를 관리를 줄이고, 선언적으로 코드를 작성할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1733393589906&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const LazyComponent = React.lazy(() =&amp;gt; import('./LazyComponent'));

function App() {
  return (
    &amp;lt;React.Suspense fallback={&amp;lt;div&amp;gt;Loading...&amp;lt;/div&amp;gt;}&amp;gt;
      &amp;lt;LazyComponent /&amp;gt;
    &amp;lt;/React.Suspense&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&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 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;5. 정리&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Fetch-on-render: 간단하지만 초기 렌더링시 로딩 상태가 보임&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Fetch-then-render: 데이터를 미리 가져오지만 초기 렌더링이 느릴 수 있음&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Render-as-you-fetch: 데이터를 가져오면서 동시에 렌더링하여 사용자 경험을 최적화&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Suspense: 비동기 렌더링 및 데이터 fetching을 선언적으로 관리하는 도구&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div id=&quot;mttContainer&quot; class=&quot;notranslate&quot; style=&quot;transform: translate(759px, 240px);&quot; aria-expanded=&quot;false&quot;&gt;&amp;nbsp;&lt;/div&gt;</description>
      <category>React/다시 공부하는 리액트</category>
      <author>오류확인자</author>
      <guid isPermaLink="true">https://whereiserror.tistory.com/290</guid>
      <comments>https://whereiserror.tistory.com/290#entry290comment</comments>
      <pubDate>Thu, 5 Dec 2024 19:14:51 +0900</pubDate>
    </item>
    <item>
      <title>React useMutation</title>
      <link>https://whereiserror.tistory.com/289</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. useMutation&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Query 라이브러리에서 제공하는 훅으로, 서버에 데이터를 생성, 수정, 삭제하는 비동기 작업을 간단하고 효과적으로 처리할 수 있도록 도와준다. 주로 POST, PUT, DELETE 요청과 같은 작업에 사용되며, React 상태 관리와 결합하여 데이터 갱신 및 UI 업데이트를 효율적으로 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1-1. 주요 특징&lt;/b&gt;&lt;/h2&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;isLoading: 요청이 진행중인지 나타냄&lt;/li&gt;
&lt;li&gt;isError: 요청이 실패했는지 나타냄&lt;/li&gt;
&lt;li&gt;isSuccess: 요청이 성공했는지 나타냄&lt;/li&gt;
&lt;/ul&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;성공 또는 실패 시 호출되는 콜백 함수를 설정할 수 있음(onSuccess, onError 등)&lt;/li&gt;
&lt;/ul&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;데이터 변경 후 React Query의 useQuery 캐시를 리패치하여 UI를 갱신할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1733302419030&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useMutation } from 'react-query';
import axios from 'axios';

function MyComponent() {
  // 서버에 데이터 전송 함수 정의
  const createUser = async (newUser) =&amp;gt; {
    return await axios.post('/api/users', newUser);
  };

  // useMutation 설정
  const mutation = useMutation(createUser, {
    onSuccess: (data) =&amp;gt; {
      console.log('성공:', data);
      // 캐시 무효화 등 추가 작업
    },
    onError: (error) =&amp;gt; {
      console.error('오류 발생:', error);
    },
  });

  const handleCreateUser = () =&amp;gt; {
    // Mutation 실행
    mutation.mutate({ name: 'John Doe', email: 'john@example.com' });
  };

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;button onClick={handleCreateUser} disabled={mutation.isLoading}&amp;gt;
        {mutation.isLoading ? '생성 중...' : '사용자 생성'}
      &amp;lt;/button&amp;gt;
      {mutation.isError &amp;amp;&amp;amp; &amp;lt;p&amp;gt;오류가 발생했습니다.&amp;lt;/p&amp;gt;}
      {mutation.isSuccess &amp;amp;&amp;amp; &amp;lt;p&amp;gt;사용자 생성 성공!&amp;lt;/p&amp;gt;}
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&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 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. useMutation 반환값&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useMutation은 다음과 같은 객체를 반환하여, 이를 통해 비동기 작업의 상태를 관리할 수 있다.&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;&lt;b&gt;2-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;isLoading: 요청이 진행 중인지 여부&lt;/li&gt;
&lt;li&gt;isError: 요청 실패 여부&lt;/li&gt;
&lt;li&gt;isSuccess: 요청 성공 여부&lt;/li&gt;
&lt;li&gt;data: 요청 성공 시 서버에서 반환된 데이터&lt;/li&gt;
&lt;li&gt;error: 요청 실패 시 발생한 오류 정보&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2-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;mutate(variables): mutation 요청을 실행&lt;/li&gt;
&lt;li&gt;mutateAsync(variables): Promise를 반환하는 비동기 메서드&lt;/li&gt;
&lt;li&gt;reset(): mutation 상태를 초기화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. 옵션&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useMutation은 두 번째 인수로 옵션 객체를 받을 수 있으며, 다음과 같은 옵션을 사용한다.&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;1. onSuccess: 요청 성공 시 호출되는 콜백 함수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. onError: 요청 실패 시 호출되는 콜백 함수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. onSettled: 성공 또는 실패 여부와 상관없이 호출되는 콜백 함수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. retry: 요청 실패 시 재시도 횟수 설정&lt;/p&gt;
&lt;pre id=&quot;code_1733302748724&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const mutation = useMutation(createUser, {
  onSuccess: () =&amp;gt; {
    console.log('요청 성공!');
  },
  onError: (error) =&amp;gt; {
    console.error('요청 실패:', error);
  },
  retry: 3, // 실패 시 최대 3번 재시도
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;예제. 캐시와 결합&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1733313550191&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useMutation, useQueryClient } from 'react-query';

function MyComponent() {
  const queryClient = useQueryClient();

  const mutation = useMutation(createUser, {
    onSuccess: () =&amp;gt; {
      queryClient.invalidateQueries('users'); // 'users' 쿼리를 리페치
    },
  });

  return &amp;lt;button onClick={() =&amp;gt; mutation.mutate(newUser)}&amp;gt;사용자 생성&amp;lt;/button&amp;gt;;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>React/다시 공부하는 리액트</category>
      <author>오류확인자</author>
      <guid isPermaLink="true">https://whereiserror.tistory.com/289</guid>
      <comments>https://whereiserror.tistory.com/289#entry289comment</comments>
      <pubDate>Wed, 4 Dec 2024 20:59:23 +0900</pubDate>
    </item>
    <item>
      <title>React useQuery</title>
      <link>https://whereiserror.tistory.com/288</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. React useQuery&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Query에서 제공하는 훅으로, 서버에서 데이터를 가져오고 이를 관리하는 강력한 도구이다. 비동기 데이터 패칭, 캐싱, 업데이트, 에러 핸들링 등을 간편하게 처리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1-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;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;/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;같은 키(queryKey)로 요청하면 이미 캐싱된 데이터를 사용하고, 필요 시 다시 패칭한다.&lt;/li&gt;
&lt;/ul&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;데이터를 갱신하거나 특정 조건에서 자동으로 다시 요청한다.&lt;/li&gt;
&lt;/ul&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;데이터 로드 상태, 에러 발생 여부를 간단하게 확인할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1-2. 기본 사용법&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1733297751527&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useQuery } from '@tanstack/react-query';
import axios from 'axios';

function ExampleComponent() {
  const fetchData = async () =&amp;gt; {
    const response = await axios.get('/api/data');
    return response.data;
  };

  const { data, isLoading, error } = useQuery(['dataKey'], fetchData);

  if (isLoading) return &amp;lt;div&amp;gt;Loading...&amp;lt;/div&amp;gt;;
  if (error) return &amp;lt;div&amp;gt;Error: {error.message}&amp;lt;/div&amp;gt;;

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;Fetched Data&amp;lt;/h1&amp;gt;
      &amp;lt;pre&amp;gt;{JSON.stringify(data, null, 2)}&amp;lt;/pre&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&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 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. 주요 옵션&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useQuery는 다음과 같은 옵션을 지원한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2-1.&amp;nbsp; queryKey(필수)&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;/li&gt;
&lt;li&gt;예: ['user', userId]&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2-2. queryFn(필수)&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;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2-3. enabled&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;true일 때만 쿼리를 실행한다. 특정 조건에서 데이터를 패칭하고 싶을 때 유용하다.&lt;/li&gt;
&lt;li&gt;예: enabled: !!userId&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2-4. staleTime&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;데이터가 Fresh 상태로 간주되는 시간을 설정한다. Fresh 상태에서는 동일한 쿼리가 다시 실행되더라도 서버 요청 없이 캐시 데이터를 반환한다.&lt;/li&gt;
&lt;li&gt;기본값: 0&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1733298996215&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;useQuery(['key'], fetchData, { staleTime: 10000 }); // 10초 동안 Fresh 상태 유지&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2-5.&amp;nbsp; cacheTime&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;캐시 데이터를 유지하는 시간(ms), 캐시 만료 후에는 데이터가 삭제된다.&lt;/li&gt;
&lt;li&gt;기본값: 5분(300,000ms)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1733299018354&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;useQuery(['key'], fetchData, { cacheTime: 60000 }); // 1분 동안 캐시 유지&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;반환 값에 Fresh와 Stale 상태&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. Fresh 상태&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;쿼리 실행 직후 staleTime이 설정된 시간 동안 데이터가 신선하다고 간주된다.&lt;/li&gt;
&lt;li&gt;이때 동일한 쿼리가 실행되면 서버 요청 없이 캐시 데이터를 반환한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. Stale 상태&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; staleTime이 지난 이후 데이터는 Stale 상태로 간주된다.&lt;/li&gt;
&lt;li&gt;동일한 쿼리가 실행되면 우선 캐시 데이터를 반환하고, 동시에 서버에 새 데이터를 요청한다.&lt;/li&gt;
&lt;li&gt;서버 요청 완료 후 새로운 데이터를 캐시를 업데이트하고, 컴포넌트를 다시 렌더링한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1733299337538&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useQuery } from '@tanstack/react-query';
import axios from 'axios';

function ExampleComponent() {
  const fetchData = async () =&amp;gt; {
    const response = await axios.get('/api/data');
    return response.data;
  };

  const { data, isLoading, error, isFetching } = useQuery(
    ['dataKey'],
    fetchData,
    {
      staleTime: 10000, // 10초 동안 Fresh 상태 유지
      cacheTime: 300000, // 5분 동안 캐시 데이터 유지
    }
  );

  if (isLoading) return &amp;lt;div&amp;gt;Loading...&amp;lt;/div&amp;gt;;
  if (error) return &amp;lt;div&amp;gt;Error: {error.message}&amp;lt;/div&amp;gt;;

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;Fetched Data&amp;lt;/h1&amp;gt;
      &amp;lt;pre&amp;gt;{JSON.stringify(data, null, 2)}&amp;lt;/pre&amp;gt;
      {isFetching &amp;amp;&amp;amp; &amp;lt;p&amp;gt;Refreshing data...&amp;lt;/p&amp;gt;}
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;캐싱 관련 설정은 staleTime과 cacheTime 옵션으로 처리된다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;staleTime: Fresh 상태 유지 시간 (서버 요청 최소화)&lt;/li&gt;
&lt;li&gt;cacheTime: 캐시 데이터를 삭제하기 전까지 유지하는 시간 (효율적인 메모리 관리)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2-6. refetchOnWindowFocus&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;/li&gt;
&lt;li&gt;기본값: true&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. 반환 값&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useQuery는 다음 데이터를 반환한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;data : 패칭된 데이터&lt;/li&gt;
&lt;li&gt;isLoading: 데이터가 로딩 중인지 확인&lt;/li&gt;
&lt;li&gt;error: 요청 실패 시 발생한 에러 객체&lt;/li&gt;
&lt;li&gt;isFetching: 백그라운드에서 데이터를 다시 가져오는 중인지 확인&lt;/li&gt;
&lt;li&gt;refetch: 데이터를 수동으로 다시 가져오는 함수&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. 고급 기능&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 무한 스크롤&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;useInfiniteQuery를 사용하여 구현&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Mutation 관리&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터를 변경하는 작업(예: POST, PUT, DELETE)는 useMutation으로 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. Devtools 지원&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;React Query Devtools를 통해 상태를 시각적으로 관리&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;6. 사용 이유&lt;/b&gt;&lt;/h2&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;/ul&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;div id=&quot;mttContainer&quot; class=&quot;notranslate&quot; style=&quot;transform: translate(832px, 169px);&quot; aria-expanded=&quot;false&quot;&gt;&amp;nbsp;&lt;/div&gt;</description>
      <category>React/다시 공부하는 리액트</category>
      <author>오류확인자</author>
      <guid isPermaLink="true">https://whereiserror.tistory.com/288</guid>
      <comments>https://whereiserror.tistory.com/288#entry288comment</comments>
      <pubDate>Wed, 4 Dec 2024 17:02:55 +0900</pubDate>
    </item>
    <item>
      <title>React Interceptor</title>
      <link>https://whereiserror.tistory.com/287</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Interceptor 의 동작 원리&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1-1. 요청 인터셉터(request interceptor)&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;API 요청이 서버로 전송되기 직전에 실행된다.&lt;/li&gt;
&lt;li&gt;주로 공통 헤더 추가, 인증 토큰 첨부, 요청 데이터 가공 등의 작업에 사용된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1733214519848&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;apiClient.interceptors.request.use(
  (config) =&amp;gt; {
    console.log('Request sent:', config);
    return config; // 요청 전 로직 추가
  },
  (error) =&amp;gt; {
    return Promise.reject(error); // 요청 에러 처리
  }
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. config&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;요청 객체를 나타낸다. 여기에는 method, url, headers, params 등이 포함된다.&lt;/li&gt;
&lt;li&gt;요청을 보내기 전에 잉 객체를 수정할 수 있다.&lt;/li&gt;
&lt;li&gt;예: 토큰을 헤더에 추가하거나 기본 값을 설정&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;이 로직은 요청을 보내기 전에 실행된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1733214608746&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;config.headers.Authorization = `Bearer ${token}`;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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;요청 설정에서 문제가 생겼을 경우, 에러를 처리하거나 반환한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1733214648652&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;return Promise.reject(new Error('Request setup failed'));&lt;/code&gt;&lt;/pre&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1-2. 응답 인터셉터(response interceptor)&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;/li&gt;
&lt;li&gt;주로 응답 데이터 변환, 에러 처리, 로깅 등에 사용된다&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1733214725779&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;apiClient.interceptors.response.use(
  (response) =&amp;gt; {
    console.log('Response received:', response);
    return response; // 응답 후 로직 추가
  },
  (error) =&amp;gt; {
    return Promise.reject(error); // 응답 에러 처리
  }
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. response&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;여기에는 status, data, headers 등이 포함된다.&lt;/li&gt;
&lt;li&gt;응답 데이터를 가공하거나 검증할 수 있다.&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; 성공적으로 받은 데이터를 변환하거나 로깅에 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1733214886970&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (response.status === 200) {
  console.log('Success:', response.data);
}
return response.data; // 필요한 데이터만 반환&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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;서버로부터 에러 응답이 온 경우, 이를 처리하거나 에러를 반환한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1733214915085&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (error.response.status === 401) {
  console.error('Unauthorized access!');
}
return Promise.reject(error);&lt;/code&gt;&lt;/pre&gt;
&lt;div id=&quot;mttContainer&quot; class=&quot;notranslate&quot; style=&quot;transform: translate(869px, 234px);&quot; aria-expanded=&quot;false&quot;&gt;&amp;nbsp;&lt;/div&gt;</description>
      <category>React</category>
      <author>오류확인자</author>
      <guid isPermaLink="true">https://whereiserror.tistory.com/287</guid>
      <comments>https://whereiserror.tistory.com/287#entry287comment</comments>
      <pubDate>Tue, 3 Dec 2024 17:35:30 +0900</pubDate>
    </item>
  </channel>
</rss>