<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>개발자일지</title>
    <link>https://itconquest.tistory.com/</link>
    <description>프론트엔드 주니어, 개발 기록</description>
    <language>ko</language>
    <pubDate>Sat, 1 Aug 2026 04:01:23 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>TTH</managingEditor>
    <image>
      <title>개발자일지</title>
      <url>https://tistory1.daumcdn.net/tistory/4865422/attach/72a78ffb588247c59d95fe87c07155bb</url>
      <link>https://itconquest.tistory.com</link>
    </image>
    <item>
      <title>[Flutter] GetX, 상태관리를 쉽게 만드는 라이브러리</title>
      <link>https://itconquest.tistory.com/entry/Flutter-GetX-%EC%83%81%ED%83%9C%EA%B4%80%EB%A6%AC%EB%A5%BC-%EC%89%BD%EA%B2%8C-%EB%A7%8C%EB%93%9C%EB%8A%94-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Flutter로 앱을 개발하다 보면 상태관리(State Management)는 반드시 고려해야 하는 요소이다.&lt;/span&gt;&lt;/p&gt;
&lt;div data-is-intersecting=&quot;true&quot; data-turn-id-container=&quot;request-6a011b56-7a5c-8321-9e84-ad4d7acc4252-8&quot;&gt;
&lt;div&gt;
&lt;div data-turn-start-message=&quot;true&quot; data-message-model-slug=&quot;gpt-5-5&quot; data-message-id=&quot;52190388-a6ab-4840-b213-7534954c6eaf&quot; data-message-author-role=&quot;assistant&quot;&gt;
&lt;div data-is-intersecting=&quot;true&quot; data-turn-id-container=&quot;request-6a011b56-7a5c-8321-9e84-ad4d7acc4252-9&quot;&gt;
&lt;div data-turn-start-message=&quot;true&quot; data-message-model-slug=&quot;gpt-5-5&quot; data-message-id=&quot;aa37c54a-6e39-4baf-a184-ae396f99a522&quot; data-message-author-role=&quot;assistant&quot;&gt;
&lt;p data-end=&quot;61&quot; data-start=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;198&quot; data-start=&quot;63&quot; data-ke-size=&quot;size16&quot;&gt;앱에서는 로그인 상태, 장바구니 수량, API 응답 데이터, 좋아요 여부, 테마 설정처럼 실행 중 계속 변경되는 데이터를 지속적으로 관리해야 한다. 이러한 상태를 어떻게 관리하느냐에 따라 코드 구조, 유지보수성, 개발 생산성이 크게 달라진다.&lt;/p&gt;
&lt;p data-end=&quot;198&quot; data-start=&quot;63&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;374&quot; data-start=&quot;200&quot; data-ke-size=&quot;size16&quot;&gt;Flutter에서는 이를 위해 다양한 상태관리 방식을 제공하며, 대표적으로 Provider, Riverpod, Bloc, GetX 등이 많이 사용된다. 그중 GetX는 비교적 적은 코드로 상태관리와 라우팅, 의존성 관리까지 처리할 수 있어 빠른 개발에 강점을 가진 라이브러리로 많이 활용되고 있다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&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;h3 data-end=&quot;280&quot; data-start=&quot;272&quot; data-section-id=&quot;49mqrb&quot; data-ke-size=&quot;size23&quot;&gt;GetX란?&lt;/h3&gt;
&lt;p data-end=&quot;350&quot; data-start=&quot;282&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;GetX&lt;/span&gt;&lt;/span&gt; 는 Flutter에서 사용하는 상태관리 라이브러리이다.&lt;/p&gt;
&lt;p data-end=&quot;374&quot; data-start=&quot;352&quot; data-ke-size=&quot;size16&quot;&gt;단순히 상태관리만 제공하는 것이 아니라 상태관리, 라우팅, 의존성 관리 기능까지 함께 제공한다. Flutter에서 자주 사용하는 기능들을 하나의 라이브러리로 처리할 수 있다는 점이 특징이다.&lt;/p&gt;
&lt;blockquote data-end=&quot;374&quot; data-start=&quot;352&quot; data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;GetX의 주요 기능&lt;/b&gt;&lt;br /&gt;- 상태관리(State Management)&lt;br /&gt;- 라우팅(Route Management)&lt;br /&gt;- 의존성 관리(Dependency Injection)&lt;/blockquote&gt;
&lt;p data-end=&quot;524&quot; data-start=&quot;472&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;524&quot; data-start=&quot;472&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;524&quot; data-start=&quot;472&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;541&quot; data-start=&quot;531&quot; data-section-id=&quot;my8tq4&quot; data-ke-size=&quot;size23&quot;&gt;GetX의 특징&lt;/h3&gt;
&lt;p data-end=&quot;557&quot; data-start=&quot;543&quot; data-section-id=&quot;gqkd96&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 코드가 간결하다&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;603&quot; data-start=&quot;559&quot; data-ke-size=&quot;size16&quot;&gt;Flutter 기본 상태관리 방식인 setState() 대비 코드량이 적다.&lt;/p&gt;
&lt;p data-end=&quot;621&quot; data-start=&quot;605&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어 상태를 선언할 때 .obs만 붙이면 반응형 상태로 사용할 수 있다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;var count = 0.obs;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;736&quot; data-start=&quot;701&quot; data-ke-size=&quot;size16&quot;&gt;복잡한 보일러플레이트 코드가 적은 편이라 빠르게 개발하기 좋다.&lt;/p&gt;
&lt;p data-end=&quot;736&quot; data-start=&quot;701&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;736&quot; data-start=&quot;701&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;768&quot; data-start=&quot;743&quot; data-section-id=&quot;giw9ll&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 상태 변경 시 UI가 자동 갱신된다&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;794&quot; data-start=&quot;770&quot; data-ke-size=&quot;size16&quot;&gt;GetX는 상태값 변경을 자동으로 감지한다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;상태 변경
&amp;rarr; UI 자동 업데이트&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;854&quot; data-start=&quot;845&quot; data-ke-size=&quot;size16&quot;&gt;형태로 동작한다.&lt;/p&gt;
&lt;p data-end=&quot;889&quot; data-start=&quot;856&quot; data-ke-size=&quot;size16&quot;&gt;따라서 setState()를 직접 호출하지 않아도 된다.&lt;/p&gt;
&lt;p data-end=&quot;889&quot; data-start=&quot;856&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;889&quot; data-start=&quot;856&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;910&quot; data-start=&quot;896&quot; data-section-id=&quot;1r73mrv&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 러닝커브가 낮다&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;957&quot; data-start=&quot;912&quot; data-ke-size=&quot;size16&quot;&gt;문법 자체가 단순한 편이라 Flutter 입문자도 비교적 빠르게 적응할 수 있다.&lt;/p&gt;
&lt;p data-end=&quot;962&quot; data-start=&quot;959&quot; data-ke-size=&quot;size16&quot;&gt;특히 개인 프로젝트, MVP 개발, 프로토타입을 제작하는 것처럼 기능 구현이 빠르게 필요한 상황에서 많이 사용된다.&lt;/p&gt;
&lt;p data-end=&quot;962&quot; data-start=&quot;959&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;962&quot; data-start=&quot;959&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;962&quot; data-start=&quot;959&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;1042&quot; data-start=&quot;1033&quot; data-section-id=&quot;1ygrfro&quot; data-ke-size=&quot;size23&quot;&gt;GetX 설치&lt;/h3&gt;
&lt;p data-end=&quot;1070&quot; data-start=&quot;1044&quot; data-ke-size=&quot;size16&quot;&gt;pubspec.yaml에 패키지를 추가한다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;dependencies:
  get: ^4.6.6&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1128&quot; data-start=&quot;1125&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1128&quot; data-start=&quot;1125&quot; data-ke-size=&quot;size16&quot;&gt;이후 아래의 명령어를 실행한다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;flutter pub get&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1184&quot; data-start=&quot;1171&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;div data-turn-start-message=&quot;true&quot; data-message-model-slug=&quot;gpt-5-5&quot; data-message-id=&quot;52190388-a6ab-4840-b213-7534954c6eaf&quot; data-message-author-role=&quot;assistant&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div data-turn-start-message=&quot;true&quot; data-message-model-slug=&quot;gpt-5-5&quot; data-message-id=&quot;52190388-a6ab-4840-b213-7534954c6eaf&quot; data-message-author-role=&quot;assistant&quot;&gt;
&lt;h3 data-end=&quot;1201&quot; data-start=&quot;1191&quot; data-section-id=&quot;qpt8uv&quot; data-ke-size=&quot;size23&quot;&gt;GetX 기본 사용 방법&lt;/h3&gt;
&lt;p data-end=&quot;1221&quot; data-start=&quot;1203&quot; data-section-id=&quot;5zuht5&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. Controller 생성&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;import 'package:get/get.dart';

class CounterController extends GetxController {

  var count = 0.obs;

  void increase() {
    count++;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1422&quot; data-start=&quot;1405&quot; data-section-id=&quot;109alg7&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1422&quot; data-start=&quot;1405&quot; data-section-id=&quot;109alg7&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f3c000;&quot;&gt;GetxController&lt;/span&gt;:&amp;nbsp;상태를 관리하는 클래스이다. React 기준으로 보면 store 혹은 state 관리 객체 느낌에 가깝다.&lt;/p&gt;
&lt;p data-end=&quot;1500&quot; data-start=&quot;1491&quot; data-section-id=&quot;1k49bhx&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f3c000;&quot;&gt;.obs: observable&lt;/span&gt; 상태를 의미한다. 즉 값이 변경되면 GetX가 이를 감지한다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;0.obs&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1555&quot; data-start=&quot;1533&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1600&quot; data-start=&quot;1588&quot; data-section-id=&quot;wilxlf&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 화면에서 사용&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;import 'package:flutter/material.dart';
import 'package:get/get.dart';

class HomeScreen extends StatelessWidget {

  final controller = Get.put(CounterController());

  HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(
        title: const Text('GetX Example'),
      ),

      body: Center(
        child: Obx(() =&amp;gt;
          Text(
            '${controller.count}',
            style: const TextStyle(fontSize: 30),
          ),
        ),
      ),

      floatingActionButton: FloatingActionButton(
        onPressed: () {
          controller.increase();
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2355&quot; data-start=&quot;2343&quot; data-section-id=&quot;130llvx&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f3c000;&quot;&gt;Get.put()&lt;/span&gt;: Controller 객체를 등록하는 역할을 한다. 화면 어디서든 동일한 Controller를 사용할 수 있게 된다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Get.put(CounterController())&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2491&quot; data-start=&quot;2483&quot; data-section-id=&quot;xezo41&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f3c000;&quot;&gt;Obx()&lt;/span&gt;: 상태 변경을 감지하는 위젯이다. count 값이 변경되면 해당 UI만 자동으로 rebuild 된다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;arcade&quot;&gt;&lt;code&gt;Obx(() =&amp;gt; Text(...))&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;2597&quot; data-start=&quot;2558&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-is-intersecting=&quot;true&quot; data-turn-id-container=&quot;request-6a011b56-7a5c-8321-9e84-ad4d7acc4252-10&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div data-turn-start-message=&quot;true&quot; data-message-model-slug=&quot;gpt-5-5&quot; data-message-id=&quot;e7f98d3d-4d97-4537-ba53-d412aacce546&quot; data-message-author-role=&quot;assistant&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p data-end=&quot;30&quot; data-start=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;GetX의 상태 업데이트 흐름은 비교적 단순한 편이다.&lt;/p&gt;
&lt;p data-end=&quot;30&quot; data-start=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;155&quot; data-start=&quot;32&quot; data-ke-size=&quot;size16&quot;&gt;사용자가 버튼을 클릭하면 Controller 내부의 상태값이 변경되고, GetX의 반응형 상태(obs)를 구독하고 있는 Obx가 해당 변경을 감지한다. 이후 변경된 상태를 기준으로 UI가 자동으로 다시 렌더링된다.&lt;/p&gt;
&lt;p data-end=&quot;155&quot; data-start=&quot;32&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;173&quot; data-start=&quot;157&quot; data-ke-size=&quot;size16&quot;&gt;즉 전체 흐름은 다음과 같다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;사용자 액션 발생
   &amp;darr;
Controller 상태 변경
   &amp;darr;
Obx가 상태 변경 감지
   &amp;darr;
필요한 UI만 rebuild&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;371&quot; data-start=&quot;277&quot; data-ke-size=&quot;size16&quot;&gt;기존 setState() 방식과 비교했을 때 명시적으로 화면 갱신 코드를 작성하지 않아도 되기 때문에 코드량이 줄어들고, 상태와 UI를 분리하기 쉽다는 장점이 있다.&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;371&quot; data-start=&quot;277&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;371&quot; data-start=&quot;277&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-end=&quot;2717&quot; data-start=&quot;2707&quot; data-section-id=&quot;dbafi&quot; data-ke-size=&quot;size23&quot;&gt;폴더 구조 예시&lt;/h3&gt;
&lt;p data-end=&quot;2754&quot; data-start=&quot;2719&quot; data-ke-size=&quot;size16&quot;&gt;GetX를 사용할 때는 보통 다음과 같은 구조를 많이 사용한다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div id=&quot;code-block-viewer&quot;&gt;
&lt;div&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;lib/
 ├── controllers/
 │    └── counter_controller.dart
 │
 ├── screens/
 │    └── home_screen.dart
 │
 ├── widgets/
 └── main.dart&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&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-end=&quot;2933&quot; data-start=&quot;2924&quot; data-section-id=&quot;1ygr799&quot; data-ke-size=&quot;size23&quot;&gt;GetX 장점&lt;/h3&gt;
&lt;div&gt;
&lt;p data-end=&quot;113&quot; data-start=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;GetX의 가장 큰 장점은 적은 코드로 상태관리와 화면 제어를 빠르게 구현할 수 있다는 점이다.&lt;br /&gt;복잡한 설정 과정이 비교적 적고 문법이 직관적인 편이라 생산성이 높은 편에 속한다.&lt;/p&gt;
&lt;p data-end=&quot;163&quot; data-start=&quot;115&quot; data-ke-size=&quot;size16&quot;&gt;특히 개인 프로젝트나 MVP 개발처럼 빠른 기능 구현이 중요한 상황에서 많이 활용된다.&lt;/p&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: 126px;&quot; border=&quot;1&quot; data-end=&quot;3086&quot; data-start=&quot;2935&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;tbody data-end=&quot;3086&quot; data-start=&quot;2957&quot;&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;장점&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot; data-end=&quot;2979&quot; data-start=&quot;2957&quot;&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;2967&quot; data-start=&quot;2957&quot;&gt;코드량이 적다&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-end=&quot;2979&quot; data-start=&quot;2967&quot; data-col-size=&quot;sm&quot;&gt;문법이 단순하다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot; data-end=&quot;3003&quot; data-start=&quot;2980&quot;&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;2993&quot; data-start=&quot;2980&quot;&gt;개발 속도가 빠르다&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-end=&quot;3003&quot; data-start=&quot;2993&quot; data-col-size=&quot;sm&quot;&gt;설정이 적다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot; data-end=&quot;3032&quot; data-start=&quot;3004&quot;&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;3017&quot; data-start=&quot;3004&quot;&gt;입문 난이도가 낮다&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-end=&quot;3032&quot; data-start=&quot;3017&quot; data-col-size=&quot;sm&quot;&gt;빠르게 적응 가능하다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot; data-end=&quot;3060&quot; data-start=&quot;3033&quot;&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;3045&quot; data-start=&quot;3033&quot;&gt;라우팅 기능 제공&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-end=&quot;3060&quot; data-start=&quot;3045&quot; data-col-size=&quot;sm&quot;&gt;화면 이동 관리 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot; data-end=&quot;3086&quot; data-start=&quot;3061&quot;&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;3073&quot; data-start=&quot;3061&quot;&gt;의존성 관리 가능&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-end=&quot;3086&quot; data-start=&quot;3073&quot; data-col-size=&quot;sm&quot;&gt;객체 재사용 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&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-end=&quot;3102&quot; data-start=&quot;3093&quot; data-section-id=&quot;1yh7o28&quot; data-ke-size=&quot;size23&quot;&gt;GetX 단점&lt;/h3&gt;
&lt;p data-end=&quot;3133&quot; data-start=&quot;3104&quot; data-section-id=&quot;165j64m&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 프로젝트 규모가 커질수록 복잡해질 수 있다&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;3153&quot; data-start=&quot;3135&quot; data-ke-size=&quot;size16&quot;&gt;GetX는 자유도가 높은 편이다.&lt;/p&gt;
&lt;p data-end=&quot;3188&quot; data-start=&quot;3155&quot; data-ke-size=&quot;size16&quot;&gt;초반에는 빠르게 개발할 수 있지만 프로젝트 규모가 커질수록 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;상태 흐름 추적이나, 구조통일, 유지보수 &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;부분에서 어려움이 생길 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;3188&quot; data-start=&quot;3155&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;3275&quot; data-start=&quot;3242&quot; data-section-id=&quot;12i2vj0&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 팀 프로젝트에서 코드 스타일 차이가 커질 수 있다&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;3300&quot; data-start=&quot;3277&quot; data-ke-size=&quot;size16&quot;&gt;GetX는 정해진 구조를 강제하지 않는다. 그래서 팀마다 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;controller 구조, &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;상태 분리 방식, &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;폴더 구조&lt;/span&gt;가 달라지는 경우가 많다.&lt;/p&gt;
&lt;p data-end=&quot;3300&quot; data-start=&quot;3277&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;3403&quot; data-start=&quot;3369&quot; data-section-id=&quot;hgq527&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 최근에는 Riverpod 선호도가 높아지는 추세이다&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;3423&quot; data-start=&quot;3405&quot; data-ke-size=&quot;size16&quot;&gt;최근 Flutter 생태계에서는 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Riverpod, &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Bloc &lt;/span&gt;선호도가 높아지는 분위기이다.&lt;/p&gt;
&lt;p data-end=&quot;3499&quot; data-start=&quot;3462&quot; data-ke-size=&quot;size16&quot;&gt;특히 규모가 큰 프로젝트일수록 구조적인 상태관리를 선호하는 편이다.&lt;/p&gt;
&lt;p data-end=&quot;3499&quot; data-start=&quot;3462&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;3499&quot; data-start=&quot;3462&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;3499&quot; data-start=&quot;3462&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;3525&quot; data-start=&quot;3506&quot; data-section-id=&quot;1lw4vst&quot; data-ke-size=&quot;size23&quot;&gt;GetX와 Riverpod 비교&lt;/h3&gt;
&lt;p data-end=&quot;92&quot; data-start=&quot;21&quot; data-ke-size=&quot;size16&quot;&gt;Flutter 상태관리 라이브러리 중 GetX와 Riverpod는 모두 많이 사용되는 편이지만, 추구하는 방향에는 차이가 있다.&lt;/p&gt;
&lt;p data-end=&quot;182&quot; data-start=&quot;94&quot; data-ke-size=&quot;size16&quot;&gt;GetX는 빠른 개발과 간결한 코드 작성에 강점이 있는 반면, Riverpod는 상태 흐름을 보다 명확하게 관리할 수 있도록 구조적인 패턴을 제공하는 편이다.&lt;/p&gt;
&lt;p data-end=&quot;182&quot; data-start=&quot;94&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;224&quot; data-start=&quot;184&quot; data-ke-size=&quot;size16&quot;&gt;프로젝트 규모나 팀 개발 여부에 따라 선택 기준도 달라지는 경우가 많다.&lt;/p&gt;
&lt;div&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: 122px;&quot; border=&quot;1&quot; data-end=&quot;455&quot; data-start=&quot;226&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;b&gt;GetX&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 17px;&quot;&gt;&lt;b&gt;Riverpod&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot; data-end=&quot;302&quot; data-start=&quot;265&quot;&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;271&quot; data-start=&quot;265&quot;&gt;난이도&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-end=&quot;282&quot; data-start=&quot;271&quot; data-col-size=&quot;sm&quot;&gt;비교적 쉬운 편&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-end=&quot;302&quot; data-start=&quot;282&quot; data-col-size=&quot;sm&quot;&gt;개념 이해가 조금 더 필요하다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot; data-end=&quot;338&quot; data-start=&quot;303&quot;&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;309&quot; data-start=&quot;303&quot;&gt;코드량&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-end=&quot;316&quot; data-start=&quot;309&quot; data-col-size=&quot;sm&quot;&gt;적은 편&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-end=&quot;338&quot; data-start=&quot;316&quot; data-col-size=&quot;sm&quot;&gt;상대적으로 코드가 늘어나는 편이다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot; data-end=&quot;367&quot; data-start=&quot;339&quot;&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;345&quot; data-start=&quot;339&quot;&gt;자유도&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-end=&quot;350&quot; data-start=&quot;345&quot; data-col-size=&quot;sm&quot;&gt;높다&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-end=&quot;367&quot; data-start=&quot;350&quot; data-col-size=&quot;sm&quot;&gt;구조적인 패턴을 지향한다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot; data-end=&quot;406&quot; data-start=&quot;368&quot;&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;376&quot; data-start=&quot;368&quot;&gt;빠른 개발&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-end=&quot;385&quot; data-start=&quot;376&quot; data-col-size=&quot;sm&quot;&gt;강점이 있다&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-end=&quot;406&quot; data-start=&quot;385&quot; data-col-size=&quot;sm&quot;&gt;가능하지만 구조 설계가 필요하다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot; data-end=&quot;455&quot; data-start=&quot;407&quot;&gt;
&lt;td style=&quot;height: 21px;&quot; data-col-size=&quot;sm&quot; data-end=&quot;418&quot; data-start=&quot;407&quot;&gt;대규모 프로젝트&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-end=&quot;437&quot; data-start=&quot;418&quot; data-col-size=&quot;sm&quot;&gt;관리 복잡도가 높아질 수 있다&lt;/td&gt;
&lt;td style=&quot;height: 21px;&quot; data-end=&quot;455&quot; data-start=&quot;437&quot; data-col-size=&quot;sm&quot;&gt;상태 흐름 관리에 유리하다&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-end=&quot;589&quot; data-start=&quot;457&quot; data-ke-size=&quot;size16&quot;&gt;GetX는 적은 코드로 빠르게 기능을 구현할 수 있어 개인 프로젝트나 MVP 개발에 적합한 편이다. 반면 Riverpod는 상태 의존성과 데이터 흐름을 명확하게 관리할 수 있어 규모가 있는 프로젝트나 협업 환경에서 선호되는 경우가 많다.&lt;/p&gt;
&lt;p data-end=&quot;589&quot; data-start=&quot;457&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;657&quot; data-start=&quot;591&quot; data-ke-size=&quot;size16&quot;&gt;최근 Flutter 생태계에서는 유지보수성과 확장성을 고려해 Riverpod를 선택하는 사례도 점차 증가하는 추세이다.&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;657&quot; data-start=&quot;591&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-end=&quot;3731&quot; data-start=&quot;3688&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;3731&quot; data-start=&quot;3688&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;3731&quot; data-start=&quot;3688&quot; data-ke-size=&quot;size16&quot;&gt;GetX는 Flutter에서 빠르게 상태관리를 구현할 수 있는 라이브러리이다.&lt;/p&gt;
&lt;p data-end=&quot;3731&quot; data-start=&quot;3688&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;3736&quot; data-start=&quot;3733&quot; data-ke-size=&quot;size16&quot;&gt;특히 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;빠른 MVP 개발, &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;개인 프로젝트, &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;간단한 서비스&lt;/span&gt;에서 생산성이 좋은 편이다.&lt;/p&gt;
&lt;p data-is-only-node=&quot;&quot; data-is-last-node=&quot;&quot; data-end=&quot;3886&quot; data-start=&quot;3788&quot; data-ke-size=&quot;size16&quot;&gt;다만 프로젝트 규모가 커질수록 상태 흐름 관리와 구조 설계가 중요해지기 때문에, 상황에 따라 Riverpod나 Bloc 같은 구조적인 상태관리 방식도 함께 고려하는 것이 좋다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발 지식모음/Flutter&amp;amp;Dart</category>
      <category>Flutter</category>
      <category>getx</category>
      <author>TTH</author>
      <guid isPermaLink="true">https://itconquest.tistory.com/206</guid>
      <comments>https://itconquest.tistory.com/entry/Flutter-GetX-%EC%83%81%ED%83%9C%EA%B4%80%EB%A6%AC%EB%A5%BC-%EC%89%BD%EA%B2%8C-%EB%A7%8C%EB%93%9C%EB%8A%94-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC#entry206comment</comments>
      <pubDate>Mon, 11 May 2026 16:38:20 +0900</pubDate>
    </item>
    <item>
      <title>[Flutter] 개념 정리 및 시작하기(window 설치, 프로젝트 생성)</title>
      <link>https://itconquest.tistory.com/entry/Flutter-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC-%EB%B0%8F-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-window-%EC%84%A4%EC%B9%98-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Flutter란?&lt;/h3&gt;
&lt;p data-end=&quot;189&quot; data-start=&quot;0&quot; data-ke-size=&quot;size16&quot;&gt;Flutter&lt;span aria-hidden=&quot;true&quot;&gt;&lt;/span&gt;는 Flutter&lt;span aria-hidden=&quot;true&quot;&gt;&lt;/span&gt;에서 개발한 크로스플랫폼 UI 프레임워크이다.&lt;br /&gt;한 번 코드를 작성하면 Android, iOS, Web, Windows, macOS까지 여러 플랫폼에서 실행할 수 있고, Dart 라는 언어를 사용해서 구현한다.&lt;/p&gt;
&lt;blockquote data-end=&quot;189&quot; data-start=&quot;0&quot; data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[ Flutter 구조 ]&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Flutter&lt;br /&gt;├─ Dart 언어&lt;br /&gt;├─ Widget(UI)&lt;br /&gt;├─ Rendering Engine(Skia)&lt;br /&gt;└─ 플랫폼(Android/iOS/Web 등)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Flutter 설치하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Flutter를 설치하기 위해서는 Git과 VS Code가 설치되어 있어야 한다.&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;먼저 해야할 일은 VS Code에 Flutter 확장 프로그램을 추가해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;마켓 플레이스 페이지&lt;/a&gt;에서 설치를 진행한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1193&quot; data-origin-height=&quot;725&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIGvFS/dJMcaiDeCNS/1ql1uk7WmEsJphMdZ2Gyw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIGvFS/dJMcaiDeCNS/1ql1uk7WmEsJphMdZ2Gyw0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIGvFS/dJMcaiDeCNS/1ql1uk7WmEsJphMdZ2Gyw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIGvFS%2FdJMcaiDeCNS%2F1ql1uk7WmEsJphMdZ2Gyw0%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;1193&quot; height=&quot;725&quot; data-origin-width=&quot;1193&quot; data-origin-height=&quot;725&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VS Code에서 Control + shift + P 를 클릭해서 명령 팔레트를 열고 flutter를 입력한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 아래와 같이 Flutter: New Project라고 나오는데 이걸 클릭한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;611&quot; data-origin-height=&quot;122&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cM81QL/dJMb997nsNn/EROBSDIiFmPmXuUHcqVsHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cM81QL/dJMb997nsNn/EROBSDIiFmPmXuUHcqVsHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cM81QL/dJMb997nsNn/EROBSDIiFmPmXuUHcqVsHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcM81QL%2FdJMb997nsNn%2FEROBSDIiFmPmXuUHcqVsHk%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;611&quot; height=&quot;122&quot; data-origin-width=&quot;611&quot; data-origin-height=&quot;122&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;&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;오른쪽 하단에 아래와 같은 창이 뜨면 Download SDK를 클릭한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Flutter를 설치할 폴더를 선택하고 다운로드를 진행하면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;462&quot; data-origin-height=&quot;118&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmsQvB/dJMcagrTjlG/fheNNO1mLb1tvDMFLiiQc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmsQvB/dJMcagrTjlG/fheNNO1mLb1tvDMFLiiQc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmsQvB/dJMcagrTjlG/fheNNO1mLb1tvDMFLiiQc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmsQvB%2FdJMcagrTjlG%2FfheNNO1mLb1tvDMFLiiQc0%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;462&quot; height=&quot;118&quot; data-origin-width=&quot;462&quot; data-origin-height=&quot;118&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;&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;그 다음에 SDK를 PATH에 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lutter SDK 경로가 환경변수(PATH)에 등록되지 않으면 아래와 같은 오류가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Windows에서 터미널에 flutter 명령어를 입력하면 운영체제는 PATH(Environment Variables)에 등록된 경로 목록에서 실행 파일을 찾는다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;flutter : 'flutter' 용어가 cmdlet, 함수, 스크립트 파일 또는 실행할 수 있는 프로그램 이름으로 인식되지 않습니다.&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;(&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Flutter뿐 아니라 Node.js, Python, Git 등의 CLI 기반 개발 도구들도 동일한 방식으로 PATH 환경변수를 통해 실행 파일 위치를 관리한다)&lt;/span&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;윈도우 키를 누르고 환경변수를 검색하여 &quot;시스템 환경 변수 편집&quot;을 클릭한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;551&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/efBC3t/dJMcabD8aYr/LmYq7Pj14ek8aSvn27J0K1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/efBC3t/dJMcabD8aYr/LmYq7Pj14ek8aSvn27J0K1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/efBC3t/dJMcabD8aYr/LmYq7Pj14ek8aSvn27J0K1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FefBC3t%2FdJMcabD8aYr%2FLmYq7Pj14ek8aSvn27J0K1%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;828&quot; height=&quot;551&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;551&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;&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;479&quot; data-origin-height=&quot;533&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cV4dZk/dJMb99TPY11/iAqWBuAsYgxKBHEC9Rt0W0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cV4dZk/dJMb99TPY11/iAqWBuAsYgxKBHEC9Rt0W0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cV4dZk/dJMb99TPY11/iAqWBuAsYgxKBHEC9Rt0W0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcV4dZk%2FdJMb99TPY11%2FiAqWBuAsYgxKBHEC9Rt0W0%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;479&quot; height=&quot;533&quot; data-origin-width=&quot;479&quot; data-origin-height=&quot;533&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 변수에서 &quot;편집&quot; 을 클릭한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;618&quot; data-origin-height=&quot;585&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l93Rs/dJMcagSYQ3G/5DCrr43Kos8rDc9afp1a7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l93Rs/dJMcagSYQ3G/5DCrr43Kos8rDc9afp1a7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l93Rs/dJMcagSYQ3G/5DCrr43Kos8rDc9afp1a7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl93Rs%2FdJMcagSYQ3G%2F5DCrr43Kos8rDc9afp1a7K%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;618&quot; height=&quot;585&quot; data-origin-width=&quot;618&quot; data-origin-height=&quot;585&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;&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;527&quot; data-origin-height=&quot;501&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lP4t4/dJMcahxAL4s/9uhBe8CGA2121DHdSGbqw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lP4t4/dJMcahxAL4s/9uhBe8CGA2121DHdSGbqw1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lP4t4/dJMcahxAL4s/9uhBe8CGA2121DHdSGbqw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlP4t4%2FdJMcahxAL4s%2F9uhBe8CGA2121DHdSGbqw1%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;527&quot; height=&quot;501&quot; data-origin-width=&quot;527&quot; data-origin-height=&quot;501&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;&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;저장하고 VS Code를 종료한 뒤 재실행하고, 터미널에서 flutter --version 를 입력해서 아래 이미지와 같이 버전이 뜨면 뜨면 flutter 설치가 완료된 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;831&quot; data-origin-height=&quot;92&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWyDhu/dJMcaf7zEsF/yB11x9MbH4mhso8xa38m9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWyDhu/dJMcaf7zEsF/yB11x9MbH4mhso8xa38m9K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWyDhu/dJMcaf7zEsF/yB11x9MbH4mhso8xa38m9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWyDhu%2FdJMcaf7zEsF%2FyB11x9MbH4mhso8xa38m9K%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;831&quot; height=&quot;92&quot; data-origin-width=&quot;831&quot; data-origin-height=&quot;92&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Flutter 프로젝트 생성하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널에 아래와 같이 입력한다.&lt;/p&gt;
&lt;pre id=&quot;code_1778218086887&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;flutter create my_app&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;프로젝트가 생성하면 아래 명령어로 방금 전 만든 프로젝트 폴더로 이동한다.&lt;/p&gt;
&lt;pre id=&quot;code_1778218140263&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cd my_app&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;아래 명령어를 입력하면 웹으로 프로젝트가 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크롬에서 실행하라고 명령했기 때문에 크롬 브라우저에서 flutter 앱이 실행된다.&lt;/p&gt;
&lt;pre id=&quot;code_1778218165411&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;flutter run -d chrome&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;* -d의 의미는 device의 약자&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;아래는 크롬으로 실행한 flutter 앱의 모습이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;618&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/376Et/dJMcabD8esE/m7WFlkp1HHWPGKg0UrhFfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/376Et/dJMcabD8esE/m7WFlkp1HHWPGKg0UrhFfK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/376Et/dJMcabD8esE/m7WFlkp1HHWPGKg0UrhFfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F376Et%2FdJMcabD8esE%2Fm7WFlkp1HHWPGKg0UrhFfK%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;500&quot; height=&quot;618&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;618&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>개발 지식모음/Flutter&amp;amp;Dart</category>
      <category>Flutter</category>
      <author>TTH</author>
      <guid isPermaLink="true">https://itconquest.tistory.com/205</guid>
      <comments>https://itconquest.tistory.com/entry/Flutter-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC-%EB%B0%8F-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-window-%EC%84%A4%EC%B9%98-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1#entry205comment</comments>
      <pubDate>Fri, 8 May 2026 14:41:57 +0900</pubDate>
    </item>
    <item>
      <title>[ReactJS] useState - 상태(state)를 관리하기 위한 Hook</title>
      <link>https://itconquest.tistory.com/entry/ReactJS-useState-%EC%83%81%ED%83%9Cstate%EB%A5%BC-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%9C-Hook</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;useState란?&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;useState&lt;/span&gt;&lt;span&gt;는 &lt;/span&gt;&lt;span&gt;컴포넌트 내부 상태(state)를 관리하기 위한 Hook이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;상태(state)란 다음과 같은 데이터를 의미한다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-spread=&quot;false&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;시간이 지나면서 변경되는 값&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;값이 변경되면 화면(UI)도 함께 업데이트됨&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;useState는&amp;nbsp;값이 변경되면 화면도 함께 업데이트되도록 만드는 React의 상태 관리 기능이다.&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;useState 기본 사용법&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;useState&lt;/span&gt;&lt;span&gt;의 기본 사용법은 함수형 컴포넌트 내부에서 상태를 선언하고, 해당 상태를 변경하는 함수를 함께 사용하는 구조이다. &lt;/span&gt;&lt;span&gt;useState&lt;/span&gt;&lt;span&gt;를 호출하면 배열 형태로 값이 반환되며, 첫 번째 요소는 현재 상태 값이고 두 번째 요소는 상태를 변경하는 함수이다. 초기값은 &lt;/span&gt;&lt;span&gt;useState(초기값)&lt;/span&gt;&lt;span&gt; 형태로 전달한다. 이후 상태를 변경할 때는 직접 값을 수정하는 것이 아니라 반드시 setter 함수를 사용해야 하며, 이 함수가 호출되면 React는 상태 변화를 감지하고 컴포넌트를 재렌더링하여 변경된 값이 화면에 반영되도록 처리한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;import { useState } from &quot;react&quot;;

function Counter() {
  const [count, setCount] = useState(0);

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;p&amp;gt;{count}&amp;lt;/p&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; setCount(count + 1)}&amp;gt;증가&amp;lt;/button&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;&lt;span&gt;- useState(0)&lt;/span&gt;&lt;span&gt; &amp;rarr; 초기값을 0으로 설정하는 함수&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;- count&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; &amp;rarr; 현재 상태 값&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;- setCount&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; &amp;rarr; 상태를 변경하는 함수&lt;/span&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;span&gt;버튼 클릭 시 &lt;/span&gt;&lt;span&gt;setCount&lt;/span&gt;&lt;span&gt;가 실행되며, 상태가 변경되고 컴포넌트가 재렌더링된다.&lt;/span&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;&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;span&gt;배열 구조를 사용하는 이유&lt;/span&gt;&lt;/h3&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;const [state, setState] = useState(initialValue);&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;span&gt;자바스크립트의 배열 구조 분해는 순서 기반으로 값 할당이 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;575&quot; data-start=&quot;561&quot; data-ke-size=&quot;size16&quot;&gt;React는 항상 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;1번째는 상태값, &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;2번째는 상태 변경 함수를 사용하기 때문에&amp;nbsp;&lt;/span&gt;이 규칙을 유지하기 때문에 배열이 자연스럽다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;const arr = [10, 20]; // [상태값, 상태변경함수]
const [a, b] = arr;&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;useState 동작 원리&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;useState&lt;/span&gt;&lt;span&gt;의 동작 흐름은 간단하게 &lt;b&gt;&amp;ldquo;값이 바뀌면 화면도 다시 그려진다&amp;rdquo;&lt;/b&gt;라고 이해하면 된다.&lt;/span&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;span&gt;컴포넌트가 처음 실행될 때 &lt;/span&gt;&lt;span&gt;useState&lt;/span&gt;&lt;span&gt;로 설정한 초기값이 상태로 저장되고 화면에 표시된다. 이후 어떤 이벤트(예: 버튼 클릭)로 인해 상태 변경 함수(&lt;/span&gt;&lt;span&gt;setState&lt;/span&gt;&lt;span&gt;)가 호출되면, React는 기존 상태와 변경된 상태를 비교하여 값이 달라졌음을 인식한다. 그러면 해당 컴포넌트를 다시 실행(재렌더링)하여 새로운 상태 값을 기반으로 UI를 다시 그린다. 그 결과, 변경된 값이 화면에 반영된다.&lt;/span&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;span&gt;즉, 상태가 바뀌는 순간 React가 자동으로 다시 렌더링을 수행하고, 최신 상태가 UI에 표시되는 구조이다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[ useState의 동작 흐름 정리 ]&lt;/b&gt;&lt;br /&gt;1) 컴포넌트 최초 렌더링 시 상태가 초기화된다&lt;br /&gt;2) setState 함수가 호출된다&lt;br /&gt;3) React가 상태 변화를 감지한다&lt;br /&gt;4) 컴포넌트가 다시 렌더링된다&lt;br /&gt;5) 변경된 값이 UI에 반영된다&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;잘못된 사용 방식과 올바른 사용방법&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; 잘못된 예)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 React가 상태 변화를 감지하지 못하기 때문에 사용하면 안 된다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;count = count + 1;&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;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;반드시 상태 변경 함수(setter)를 통해 값을 변경해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;setCount(count + 1);&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;React는 내부적으로 언제 다시 렌더링 해야하는지 판단하는데, 그 기준이 setter 함수(setCount)가 호출되었는지의 여부이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;count는 단순한 변수처럼 보이지만 실제로는 react가 관리하는 값이다. 그런데 count = count + 1처럼 직접 값을 바꾸면 react 입장에서는 값이 바뀌었는지 알 방법이 없다.&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;setCount를 호출하는건 단순히 값을 바꾸는 것이 아니라 react에게 상태가 변경되었다는 신호를 보내는 행위이다.&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;실무에서 자주 하는 실수, 이전 상태 기반 업데이트&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;잘못된 예, 연속 업데이트 문제)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;아래와 같이 하면 2가 아니라 1만 증가할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;count는 현재 랜더링 기준 값이고, 두 번 모두 같은 값을 기준으로 계산된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1776299242610&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;setCount(count + 1);
setCount(count + 1);&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;&lt;b&gt;&lt;span&gt;올바른 예)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;setCount(prev =&amp;gt; prev + 1);
setCount(prev =&amp;gt; prev + 1);&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;span&gt;이 방식은 &lt;/span&gt;&lt;span&gt;React는 상태 업데이트를 비동기적으로 처리하고, 여&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;러 번 상태를 변경할 때 안전하기 때문에 권장되는 방식이다.&lt;/span&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;&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;span&gt;다양한 데이터 타입 저장&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;useState&lt;/span&gt;&lt;span&gt;는 다양한 타입의 데이터를 저장할 수 있다.&lt;/span&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;b&gt;&lt;span&gt;숫자&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;const [count, setCount] = useState(0);&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;span&gt;문자열&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;sqf&quot;&gt;&lt;code&gt;const [name, setName] = useState(&quot;&quot;);&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;span&gt;객체&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;const [user, setUser] = useState({ name: &quot;홍길동&quot;, age: 20 });&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;setUser({ ...user, age: 21 });&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;span&gt;객체를 업데이트할 때는 반드시 &lt;/span&gt;&lt;b&gt;&lt;span&gt;불변성 유지&lt;/span&gt;&lt;/b&gt;&lt;span&gt;를 위해 spread 문법을 사용해야 한다.&lt;/span&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;&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;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useState는 React에서 상태를 관리하기 위한 Hook이다. 상태 값이 변경되면 React는 해당 컴포넌트를 자동으로 재렌더링하여 변경된 내용을 UI에 반영한다. 이때 상태는 직접 수정하는 것이 아니라 반드시 setter 함수를 통해 변경해야 하며, 특히 이전 상태를 기반으로 값을 업데이트하는 방식(prev =&amp;gt; ...&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;)을 사용하는 것이 안전하다. 또한&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;useState는 숫자, 문자열, 객체, 배열 등 다양한 타입의 데이터를 상태로 저장하고 관리할 수 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[ 핵심 정리 ]&lt;/b&gt;&lt;br /&gt;useState는 상태를 관리하는 Hook이다&lt;br /&gt;상태가 변경되면 컴포넌트가 자동으로 재렌더링된다&lt;br /&gt;상태는 반드시 setter 함수를 통해 변경해야 한다&lt;br /&gt;이전 상태 기반 업데이트 방식이 안전하다&lt;br /&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;</description>
      <category>개발 지식모음/ReactJS&amp;amp;TypeScript</category>
      <category>Hook</category>
      <category>react</category>
      <category>reacthook</category>
      <category>useState</category>
      <category>상태관리</category>
      <author>TTH</author>
      <guid isPermaLink="true">https://itconquest.tistory.com/204</guid>
      <comments>https://itconquest.tistory.com/entry/ReactJS-useState-%EC%83%81%ED%83%9Cstate%EB%A5%BC-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%9C-Hook#entry204comment</comments>
      <pubDate>Thu, 16 Apr 2026 09:34:12 +0900</pubDate>
    </item>
    <item>
      <title>개발자가 꼭 알아야 할 Vim 최소 명령어 5가지</title>
      <link>https://itconquest.tistory.com/entry/%EA%B0%9C%EB%B0%9C%EC%9E%90%EA%B0%80-%EA%BC%AD-%EC%95%8C%EC%95%84%EC%95%BC-%ED%95%A0-Vim-%EC%B5%9C%EC%86%8C-%EB%AA%85%EB%A0%B9%EC%96%B4-5%EA%B0%80%EC%A7%80</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Vim을 처음 접하면 &amp;ldquo;왜 아무것도 입력되지 않는 것인가?&amp;rdquo;라는 당황스러운 상황을 마주하게 된다. 일반적인 에디터와 달리 Vim은 실행하자마자 바로 글을 입력할 수 있는 구조가 아니라, &amp;lsquo;모드&amp;rsquo;라는 개념을 기반으로 동작하기 때문이다. 이 차이를 이해하지 못하면 키를 눌러도 반응이 없는 것처럼 느껴지고, 오히려 더 복잡하고 어렵게 느껴질 수 있다.&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;하지만 Vim의 핵심은 생각보다 단순하다. 입력 모드와 명령 모드라는 기본 개념만 이해하고, 자주 사용하는 최소한의 명령어 몇 가지만 익히면 Git 커밋 메시지 작성, 서버 환경에서의 파일 수정, CLI 기반 작업 등 다양한 개발 상황에서 막힘 없이 사용할 수 있게 된다. 결국 Vim은 어려운 도구가 아니라, 사용 방식이 조금 다를 뿐인 효율적인 에디터이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[ 핵심요약 ]&lt;/b&gt;&lt;br /&gt;입력 안 되면 &amp;gt; i&lt;br /&gt;이상해지면 &amp;gt; Esc&lt;br /&gt;끝낼 때 &amp;gt; :wq&lt;br /&gt;취소 &amp;gt; :q!&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;1. 입력 모드 진입 &amp;mdash; &lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Vim은 실행하자마자 바로 글을 입력할 수 있는 일반적인 에디터와 달리, 기본적으로는 &amp;lsquo;명령 모드(Normal Mode)&amp;rsquo; 상태로 시작한다. 이 상태에서는 키보드를 눌러도 글자가 입력되는 것이 아니라, 커서 이동이나 삭제와 같은 명령이 실행된다. 그래서 처음 사용하는 경우 &amp;ldquo;왜 아무것도 안 써지지?&amp;rdquo;라는 혼란을 느끼기 쉽다.&lt;/span&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;span&gt;이때 필요한 것이 바로 입력 모드(Insert Mode)이다. 입력 모드로 전환해야만 우리가 익숙하게 사용하는 텍스트 입력이 가능해진다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;입력 모드로 들어가는 가장 기본적인 방법은 &lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt; 키를 누르는 것이다. &lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;는 현재 커서 위치에서 바로 입력을 시작할 수 있게 해주며, 가장 자주 사용되는 명령어이다.&lt;/span&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;span&gt;즉, Vim에서 글을 작성하기 위한 흐름은 다음과 같다. 먼저 &lt;/span&gt;&lt;span&gt;i&lt;/span&gt;&lt;span&gt;를 눌러 입력 모드로 전환한 뒤 내용을 작성하고, 입력이 끝나면 &lt;/span&gt;&lt;span&gt;Esc&lt;/span&gt;&lt;span&gt;를 눌러 다시 명령 모드로 돌아온다.&lt;/span&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;span&gt;이 과정을 이해하는 것이 Vim 사용의 첫걸음이며, 사실상 가장 중요한 핵심 개념이라고 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;i

// i 누르면 바로 글 작성 가능&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;&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;span&gt; 입력 모드 종료 &amp;mdash; &lt;/span&gt;&lt;span&gt;Esc&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Vim에서 글을 입력하는 동안에는 &amp;lsquo;입력 모드(Insert Mode)&amp;rsquo; 상태에 있게 된다. 이 상태에서는 우리가 일반적으로 사용하는 에디터처럼 키보드를 누르면 그대로 글자가 입력된다. 하지만 이 상태에서는 저장, 종료, 삭제와 같은 다양한 명령어를 사용할 수 없다.&lt;/span&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;span&gt;Vim의 대부분 기능은 &amp;lsquo;명령 모드(Normal Mode)&amp;rsquo;에서 동작하기 때문에, 원하는 작업을 수행하려면 반드시 입력 모드에서 빠져나와야 한다. 이때 사용하는 키가 바로 &lt;/span&gt;&lt;span&gt;Esc&lt;/span&gt;&lt;span&gt;이다.&lt;/span&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;span&gt;Esc&lt;/span&gt;&lt;span&gt; 키를 누르면 현재 어떤 입력 상태에 있든지 즉시 명령 모드로 돌아가게 된다. 이 상태에서는 파일 저장(&lt;/span&gt;&lt;span&gt;:wq&lt;/span&gt;&lt;span&gt;), 종료(&lt;/span&gt;&lt;span&gt;:q&lt;/span&gt;&lt;span&gt;), 강제 종료(&lt;/span&gt;&lt;span&gt;:q!&lt;/span&gt;&lt;span&gt;), 줄 삭제(&lt;/span&gt;&lt;span&gt;dd&lt;/span&gt;&lt;span&gt;) 등 다양한 명령어를 실행할 수 있다.&lt;/span&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;span&gt;특히 Vim을 사용할 때 문제가 생겼다고 느껴질 경우, 가장 먼저 눌러야 할 키도 &lt;/span&gt;&lt;span&gt;Esc&lt;/span&gt;&lt;span&gt;이다. 잘못된 모드에 들어가 있거나 예상과 다른 동작이 발생할 때, &lt;/span&gt;&lt;span&gt;Esc&lt;/span&gt;&lt;span&gt;를 누르면 기본 상태로 돌아가기 때문에 대부분의 상황을 안정적으로 정리할 수 있다.&lt;/span&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;span&gt;따라서 &lt;/span&gt;&lt;span&gt;Esc&lt;/span&gt;&lt;span&gt;는 단순히 입력을 끝내는 키가 아니라, Vim을 사용하는 전체 흐름에서 중심이 되는 매우 중요한 키라고 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;Esc

// 모든 문제의 시작과 끝 (막히면 무조건 Esc)&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;&amp;nbsp;저장하고 종료 &amp;mdash; &lt;/span&gt;&lt;span&gt;:wq&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Vim에서 작업한 내용을 파일에 반영하고 편집기를 종료하려면 반드시 저장 명령어를 사용해야 한다. 이때 가장 많이 사용되는 명령어가 바로 &lt;/span&gt;&lt;span&gt;:wq&lt;/span&gt;&lt;span&gt;이다.&lt;/span&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;span&gt;:wq&lt;/span&gt;&lt;span&gt;는 두 가지 동작을 한 번에 수행하는 명령어이다. 먼저 &lt;/span&gt;&lt;span&gt;w(write)&lt;/span&gt;&lt;span&gt;는 현재까지 작성한 내용을 파일에 저장하는 역할을 하고, &lt;/span&gt;&lt;span&gt;q(quit)&lt;/span&gt;&lt;span&gt;는 Vim을 종료하는 역할을 한다. 즉, &lt;/span&gt;&lt;span&gt;:wq&lt;/span&gt;&lt;span&gt;는 &amp;ldquo;저장하고 종료한다&amp;rdquo;는 의미를 가진다.&lt;/span&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;span&gt;이 명령어는 반드시 명령 모드(Normal Mode)에서 입력해야 한다. 따라서 먼저 &lt;/span&gt;&lt;span&gt;Esc&lt;/span&gt;&lt;span&gt;를 눌러 입력 모드에서 빠져나온 뒤, &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;를 입력하면 화면 하단에 명령어를 입력할 수 있는 영역이 나타난다. 그 다음 &lt;/span&gt;&lt;span&gt;wq&lt;/span&gt;&lt;span&gt;를 입력하고 Enter를 누르면 저장과 동시에 Vim이 종료된다.&lt;/span&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;span&gt;Git에서 merge나 commit을 수행할 때 자동으로 Vim이 열리는 경우가 많은데, 이때도 동일하게 &lt;/span&gt;&lt;span&gt;:wq&lt;/span&gt;&lt;span&gt;를 입력하면 메시지가 저장되면서 작업이 정상적으로 완료된다. 만약 이 과정을 모르고 창을 닫아버리면 작업이 취소될 수 있기 때문에 반드시 알아두어야 한다.&lt;/span&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;span&gt;따라서 &lt;/span&gt;&lt;span&gt;:wq&lt;/span&gt;&lt;span&gt;는 Vim을 사용하는 과정에서 가장 자주 사용되는 핵심 명령어이며, 사실상 Vim 사용의 마무리를 담당하는 필수 명령어라고 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;:wq

// 저장 + 종료 한 번에 처리
// Git merge, commit 할 때 필수&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&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 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;저장 없이 종료 &amp;mdash; &lt;/span&gt;&lt;span&gt;:q!&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Vim을 사용하다 보면 파일을 수정했지만 그 내용을 저장하지 않고 종료해야 하는 상황이 발생할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;예를 들어, 실수로 파일을 열었거나 변경 내용을 적용하고 싶지 않을 때가 그렇다. 이때 사용하는 명령어가 바로 &lt;/span&gt;&lt;span&gt;:q!&lt;/span&gt;&lt;span&gt;이다.&lt;/span&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;span&gt;:q!&lt;/span&gt;&lt;span&gt;는 강제로 종료하는 명령어로, 현재까지의 변경 사항을 모두 무시하고 Vim을 종료한다. 여기서 &lt;/span&gt;&lt;span&gt;q(quit)&lt;/span&gt;&lt;span&gt;는 종료를 의미하고, &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;는 강제 실행을 의미한다. 즉, 저장 여부를 묻지 않고 바로 종료하겠다는 뜻이다.&lt;/span&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;span&gt;이 명령어 역시 명령 모드(Normal Mode)에서 입력해야 한다. 먼저 &lt;/span&gt;&lt;span&gt;Esc&lt;/span&gt;&lt;span&gt;를 눌러 입력 모드에서 빠져나온 뒤, &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;를 입력하고 &lt;/span&gt;&lt;span&gt;q!&lt;/span&gt;&lt;span&gt;를 입력한 후 Enter를 누르면 즉시 Vim이 종료된다.&lt;/span&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;span&gt;특히 Git 작업 중 커밋 메시지 작성 화면이나 merge 메시지 입력 화면에서 &amp;ldquo;이 작업을 취소하고 싶다&amp;rdquo;는 경우에도 &lt;/span&gt;&lt;span&gt;:q!&lt;/span&gt;&lt;span&gt;를 사용하면 해당 작업을 저장하지 않고 빠져나올 수 있다. 잘못된 메시지를 입력했거나, 작업을 다시 진행하고 싶을 때 유용하게 사용할 수 있다.&lt;/span&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;span&gt;따라서 &lt;/span&gt;&lt;span&gt;:q!&lt;/span&gt;&lt;span&gt;는 단순한 종료 명령어가 아니라, 실수를 되돌리고 안전하게 작업을 취소할 수 있도록 도와주는 중요한 명령어라고 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;:q!

// 변경 내용 무시하고 강제 종료&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;&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;span&gt;한 줄 삭제 &amp;mdash; &lt;/span&gt;&lt;span&gt;dd&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Vim에서 코드를 수정하다 보면 특정 줄을 통째로 삭제해야 하는 경우가 자주 발생한다. 이때 사용하는 가장 기본적인 명령어가 바로 &lt;/span&gt;&lt;span&gt;dd&lt;/span&gt;&lt;span&gt;이다.&lt;/span&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;span&gt;dd&lt;/span&gt;&lt;span&gt;는 현재 커서가 위치한 한 줄 전체를 삭제하는 명령어이다. 단순히 글자를 하나씩 지우는 것이 아니라, 줄 단위로 빠르게 제거할 수 있기 때문에 코드 정리나 수정 작업에서 매우 효율적이다.&lt;/span&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;span&gt;이 명령어는 명령 모드(Normal Mode)에서 사용해야 하므로, 먼저 &lt;/span&gt;&lt;span&gt;Esc&lt;/span&gt;&lt;span&gt;를 눌러 입력 모드에서 빠져나온 뒤 &lt;/span&gt;&lt;span&gt;dd&lt;/span&gt;&lt;span&gt;를 입력하면 된다. 입력과 동시에 해당 줄이 즉시 삭제된다.&lt;/span&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;span&gt;또한 &lt;/span&gt;&lt;span&gt;dd&lt;/span&gt;&lt;span&gt;는 단순 삭제뿐만 아니라 &amp;ldquo;잘라내기(cut)&amp;rdquo; 개념으로 동작한다. 삭제된 내용은 Vim 내부 버퍼에 저장되기 때문에, 이후 &lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; 명령어를 사용하면 해당 내용을 다시 붙여넣을 수도 있다. 즉, &lt;/span&gt;&lt;span&gt;dd&lt;/span&gt;&lt;span&gt;는 줄 삭제와 동시에 이동까지 가능한 강력한 명령어이다.&lt;/span&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;span&gt;추가로, 숫자와 함께 사용하면 여러 줄을 한 번에 삭제할 수도 있다. 예를 들어 &lt;/span&gt;&lt;span&gt;3dd&lt;/span&gt;&lt;span&gt;를 입력하면 현재 줄부터 아래로 3줄이 한 번에 삭제된다.&lt;/span&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;span&gt;따라서 &lt;/span&gt;&lt;span&gt;dd&lt;/span&gt;&lt;span&gt;는 단순하지만 활용도가 매우 높은 명령어로, Vim에서의 코드 수정 속도를 크게 높여주는 핵심 기능 중 하나라고 할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;dd

// 현재 줄 통째로 삭제&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;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;</description>
      <category>개발 지식모음/기타</category>
      <category>CLI</category>
      <category>GIT</category>
      <category>Merge</category>
      <category>vim</category>
      <category>개발자기초</category>
      <category>터미널</category>
      <author>TTH</author>
      <guid isPermaLink="true">https://itconquest.tistory.com/203</guid>
      <comments>https://itconquest.tistory.com/entry/%EA%B0%9C%EB%B0%9C%EC%9E%90%EA%B0%80-%EA%BC%AD-%EC%95%8C%EC%95%84%EC%95%BC-%ED%95%A0-Vim-%EC%B5%9C%EC%86%8C-%EB%AA%85%EB%A0%B9%EC%96%B4-5%EA%B0%80%EC%A7%80#entry203comment</comments>
      <pubDate>Wed, 25 Mar 2026 10:27:49 +0900</pubDate>
    </item>
    <item>
      <title>Google Search Console TXT 인증 오류 해결 방법</title>
      <link>https://itconquest.tistory.com/entry/Google-Search-Console-TXT-%EC%9D%B8%EC%A6%9D-%EC%98%A4%EB%A5%98-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Google Search Console TXT 인증 오류 해결 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Google&amp;nbsp;Search&amp;nbsp;Console에서&amp;nbsp;도메인을&amp;nbsp;인증하려고&amp;nbsp;할&amp;nbsp;때&amp;nbsp;아래와&amp;nbsp;같은&amp;nbsp;오류가&amp;nbsp;발생하는&amp;nbsp;경우가&amp;nbsp;있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;확인 방법:&lt;br /&gt;도메인&amp;nbsp;이름&amp;nbsp;공급업체&lt;br /&gt;&lt;br /&gt;실패&amp;nbsp;이유:&lt;br /&gt;도메인의&amp;nbsp;TXT&amp;nbsp;레코드에서&amp;nbsp;인증&amp;nbsp;토큰을&amp;nbsp;찾을&amp;nbsp;수&amp;nbsp;없습니다.&amp;nbsp;변경된&amp;nbsp;DNS가&amp;nbsp;표시되는&amp;nbsp;데는&amp;nbsp;시간이&amp;nbsp;소요될&amp;nbsp;수&amp;nbsp;있습니다.&amp;nbsp;몇&amp;nbsp;시간&amp;nbsp;정도&amp;nbsp;기다린&amp;nbsp;후&amp;nbsp;Search&amp;nbsp;Console에서&amp;nbsp;속성을&amp;nbsp;다시&amp;nbsp;열어&amp;nbsp;보세요.&amp;nbsp;인증에&amp;nbsp;다시&amp;nbsp;실패하면&amp;nbsp;다른&amp;nbsp;DNS&amp;nbsp;TXT&amp;nbsp;레코드를&amp;nbsp;추가해&amp;nbsp;보시기&amp;nbsp;바랍니다.&lt;/blockquote&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-origin-width=&quot;679&quot; data-origin-height=&quot;438&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cET6aP/dJMcad1u3Eb/fdtMeMqbkKeZVi1bTx55JK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cET6aP/dJMcad1u3Eb/fdtMeMqbkKeZVi1bTx55JK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cET6aP/dJMcad1u3Eb/fdtMeMqbkKeZVi1bTx55JK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcET6aP%2FdJMcad1u3Eb%2FfdtMeMqbkKeZVi1bTx55JK%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;679&quot; height=&quot;438&quot; data-origin-width=&quot;679&quot; data-origin-height=&quot;438&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;&lt;br /&gt;이&amp;nbsp;오류는&amp;nbsp;Search&amp;nbsp;Console이&amp;nbsp;도메인&amp;nbsp;DNS에&amp;nbsp;등록된&amp;nbsp;TXT&amp;nbsp;레코드를&amp;nbsp;아직&amp;nbsp;찾지&amp;nbsp;못했을&amp;nbsp;때&amp;nbsp;발생하는&amp;nbsp;일반적인&amp;nbsp;현상이다. &lt;br /&gt;아래는&amp;nbsp;이&amp;nbsp;오류가&amp;nbsp;발생하는&amp;nbsp;이유와&amp;nbsp;해결&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;h3 data-ke-size=&quot;size23&quot;&gt;TXT 레코드가 루트 도메인에 추가되지 않은 경우&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Google Search Console의 도메인 속성 인증은 항상 루트 도메인(예: my-domain.com) 의 TXT 레코드를 검사한다. &lt;br /&gt;서브도메인(test.my-domain.com)에 TXT를 추가하면 절대 인증되지 않는다. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;해결 방법&lt;/b&gt;&lt;br /&gt;DNS 설정에서 TXT 레코드의 이름(Host)을 반드시 아래 중 하나로 설정해야 한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;@&lt;br /&gt;my-domain.com&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;h3 data-ke-size=&quot;size23&quot;&gt;TXT 레코드가 이미 존재해 추가할 수 없는 경우&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도메인 업체에 따라 동일한 이름의 TXT 레코드를 여러 개 생성하지 못하는 경우가 있다. 이때는&amp;nbsp;기존&amp;nbsp;TXT&amp;nbsp;레코드를&amp;nbsp;수정하여&amp;nbsp;값만&amp;nbsp;추가해야&amp;nbsp;한다. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;예시&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;기존 값 예시:&lt;br /&gt;v=spf1&amp;nbsp;include:_spf.google.com&amp;nbsp;~all&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;&lt;b&gt;Google 인증 값을 추가한 형태&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;v=spf1 include:_spf.google.com ~all&lt;br /&gt;google-site-verification=JRbzFIhxsxPrxM117cMZN6g4_nVd3ZlwHBmkmHD7yIo&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;h3 data-ke-size=&quot;size23&quot;&gt;DNS 전파가 완료되지 않은 경우&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DNS 변경 사항이 전 세계 DNS 서버에 반영되는 데는 시간이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도메인&amp;nbsp;업체&amp;nbsp;및&amp;nbsp;TTL&amp;nbsp;값에&amp;nbsp;따라&amp;nbsp;다르지만&amp;nbsp;보통&amp;nbsp;다음과&amp;nbsp;같은&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;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;상황&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;소요 시간&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;평균&amp;nbsp;전파&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;5분 ~ 1시간&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;늦을&amp;nbsp;경우&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;6시간 ~ 24시간&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;최악의&amp;nbsp;경우&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;최대 48시간&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;TXT 값이 정상적으로 등록되었더라도 Google이 아직 해당 값을 조회하지 못했기 때문에 오류가 나타날 수 있다.&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;h3 data-ke-size=&quot;size23&quot;&gt;DNS에 TXT가 제대로 들어갔는지 확인하는 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DNS가 제대로 적용되었는지 확인하기 위해서는 Google Admin Toolbox의 DIG 도구를 사용하는 것이 가장 정확하다. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;확인 순서&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;1. https://toolbox.googleapps.com/apps/dig 접속&lt;br /&gt;2. 검색 창에 my-domain.com 입력&lt;br /&gt;3. Record&amp;nbsp;Type을&amp;nbsp;TXT로&amp;nbsp;선택&lt;br /&gt;4. 결과에서&amp;nbsp;google-site-verification=...&amp;nbsp;값이&amp;nbsp;보이는지&amp;nbsp;확인&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;만약 해당 TXT 값이 보이지 않는다면 DNS가 아직 반영되지 않은 것이고, 보이는데도 Search Console 인증이 실패한다면 약간 더 기다리면 된다.&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;h3 data-ke-size=&quot;size23&quot;&gt;그래도 인증이 안 되는 경우&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 문제들이 있을 수 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;- TXT 값에 공백이나 오타가 있음&lt;br /&gt;- TXT 값에 따옴표를 넣어 입력해야 하는데 넣지 않았거나 반대의 경우&lt;br /&gt;- TTL 값이 지나치게 길게 설정됨&lt;br /&gt;- Cloudflare를 사용하는 경우 &amp;ldquo;프록시&amp;rdquo;가 활성화된 상태&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;이 경우 설정을 다시 확인하거나 오류 부분을 수정해야 한다.&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;h3 data-ke-size=&quot;size23&quot;&gt;결론&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 오류는 대부분 DNS 반영 지연, 잘못된 TXT 위치, 기존 TXT 레코드 오입력 때문에 발생하는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DNS&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;/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>구글&amp;amp;SEO</category>
      <category>TXT인증오류</category>
      <category>구글서치콘솔</category>
      <author>TTH</author>
      <guid isPermaLink="true">https://itconquest.tistory.com/202</guid>
      <comments>https://itconquest.tistory.com/entry/Google-Search-Console-TXT-%EC%9D%B8%EC%A6%9D-%EC%98%A4%EB%A5%98-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95#entry202comment</comments>
      <pubDate>Mon, 29 Dec 2025 17:02:29 +0900</pubDate>
    </item>
    <item>
      <title>프론트엔드 개발자가 알아야 할 회원가입 시 이메일 대소문자 처리 기준</title>
      <link>https://itconquest.tistory.com/entry/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90%EA%B0%80-%EC%95%8C%EC%95%84%EC%95%BC-%ED%95%A0-%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85-%EC%8B%9C-%EC%9D%B4%EB%A9%94%EC%9D%BC-%EB%8C%80%EC%86%8C%EB%AC%B8%EC%9E%90-%EC%B2%98%EB%A6%AC-%EA%B8%B0%EC%A4%80</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;회원가입과 로그인 기능을 구현하다 보면 한 번쯤 고민하게 되는 문제가 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이메일은 대소문자를 구분해야 할까?&lt;/span&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;결론부터 말하면, 프론트엔드에서는 이메일을 항상 소문자로 통일해서 처리하는 것이 실무적으로 가장 안전한 선택이다. 이 글에서는 그 이유와 프론트엔드에서의 올바른 처리 방법을 정리해보겠다.&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&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;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이메일&amp;nbsp;주소는&amp;nbsp;RFC&amp;nbsp;표준에&amp;nbsp;따라&amp;nbsp;@&amp;nbsp;기호를&amp;nbsp;기준으로&amp;nbsp;두&amp;nbsp;부분으로&amp;nbsp;나뉜다.&amp;nbsp;앞부분인&amp;nbsp;local-part는&amp;nbsp;이론적으로&amp;nbsp;대소문자를&amp;nbsp;구분할&amp;nbsp;수&amp;nbsp;있도록&amp;nbsp;정의되어&amp;nbsp;있으며,&amp;nbsp;뒷부분인&amp;nbsp;domain-part는&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;Test@Example.com과&amp;nbsp;test@example.com은&amp;nbsp;기술적으로&amp;nbsp;서로&amp;nbsp;다른&amp;nbsp;이메일&amp;nbsp;주소가&amp;nbsp;될&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp;local-part인&amp;nbsp;Test와&amp;nbsp;test가&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;대부분의&amp;nbsp;이메일&amp;nbsp;제공자가&amp;nbsp;local-part&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;RFC&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;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;local-part@domain-part&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;local-part (@ 앞 부분): 이론적으로 대소문자 구분 가능&lt;br /&gt;domain-part (@ 뒤 부분): 대소문자 구분 불가&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;h3 data-ke-size=&quot;size23&quot;&gt;하지만 실무에서는 다르다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현실적으로는 Gmail, Naver, Kakao, Outlook 등 대부분의 이메일 서비스가 대소문자를 구분하지 않는다. 사용자 역시 대소문자가 다르면 다른 계정이라고 인식하지 않는다. 이로&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;만약 회원가입은 Test@email.com으로 하고, 로그인은 test@email.com으로 한다면 &amp;ldquo;존재하지 않는 계정&amp;rdquo;으로 오류 발생하고 실무에서는&amp;nbsp;이런&amp;nbsp;동작을&amp;nbsp;버그로&amp;nbsp;본다.&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-origin-width=&quot;493&quot; data-origin-height=&quot;612&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brYWFy/dJMb9953qTC/4nX4KHzXXIKOtrcQyLolFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brYWFy/dJMb9953qTC/4nX4KHzXXIKOtrcQyLolFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brYWFy/dJMb9953qTC/4nX4KHzXXIKOtrcQyLolFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrYWFy%2FdJMb9953qTC%2F4nX4KHzXXIKOtrcQyLolFk%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;493&quot; height=&quot;612&quot; data-origin-width=&quot;493&quot; data-origin-height=&quot;612&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;br /&gt;&lt;br /&gt;프론트엔드에서의 권장 처리 방식&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;이메일은 항상 소문자로 정규화(normalize)하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드에서는&amp;nbsp;이메일을&amp;nbsp;사용자&amp;nbsp;입력값&amp;nbsp;그대로&amp;nbsp;서버에&amp;nbsp;전달하기보다,&amp;nbsp;항상&amp;nbsp;소문자로&amp;nbsp;정규화(normalize)한&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;오류를&amp;nbsp;예방할&amp;nbsp;수&amp;nbsp;있다. &lt;br /&gt;&lt;br /&gt;이메일&amp;nbsp;정규화&amp;nbsp;과정에서는&amp;nbsp;두&amp;nbsp;가지&amp;nbsp;처리가&amp;nbsp;핵심이다.&amp;nbsp;먼저&amp;nbsp;trim()을&amp;nbsp;사용해&amp;nbsp;앞뒤에&amp;nbsp;포함된&amp;nbsp;불필요한&amp;nbsp;공백을&amp;nbsp;제거하고,&amp;nbsp;이어서&amp;nbsp;toLowerCase()를&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;/p&gt;
&lt;pre id=&quot;code_1766560737115&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const normalizedEmail = email.trim().toLowerCase();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;trim() : 불필요한 공백 제거&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;toLowerCase() : 대소문자 통일&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;h4 data-ke-size=&quot;size20&quot;&gt;회원가입 / 로그인 / 중복 체크 모두 동일한 규칙 사용&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이메일&amp;nbsp;처리&amp;nbsp;규칙은&amp;nbsp;특정&amp;nbsp;기능에만&amp;nbsp;적용해서는&amp;nbsp;안&amp;nbsp;되며,&amp;nbsp;회원가입&amp;middot;로그인&amp;middot;이메일&amp;nbsp;중복&amp;nbsp;체크&amp;middot;비밀번호&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;상황과&amp;nbsp;같은&amp;nbsp;문제가&amp;nbsp;발생할&amp;nbsp;수&amp;nbsp;있다. &lt;br /&gt;&lt;br /&gt;따라서 모든 요청에서 이메일을 동일한 방식으로 정규화한 뒤 서버에 전달해야 한다. 예를 들어 중복 체크 역시 회원가입이나 로그인과 동일하게 소문자로 변환된 값을 기준으로 수행해야 한다. 이처럼 규칙을 일관되게 유지하지 않으면 사용자는 시스템이 불안정하다고 느끼게 되며, 전반적인 사용자 경험에도 부정적인 영향을 미치게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1766560790277&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;await checkEmail(email.trim().toLowerCase());&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;UI 표시와 인증 로직은 분리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UX 측면에서는 두 가지 선택이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력창에 바로 소문자로 변환하거나, 입력값은 그대로 두고 전송 시점에만 변환하는 방법이다. 실무에서는&amp;nbsp;보통&amp;nbsp;전송&amp;nbsp;직전에만&amp;nbsp;소문자로&amp;nbsp;변환하는&amp;nbsp;방식을&amp;nbsp;많이&amp;nbsp;사용한다. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;React + TypeScript 기준 추천 유틸 함수&lt;/b&gt;&lt;br /&gt;여러 곳에서 동일한 처리를 하기 위해 유틸 함수로 분리하는 것이 좋다.&lt;/p&gt;
&lt;pre id=&quot;code_1766560512022&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const normalizeEmail = (email: string) =&amp;gt;
  email.trim().toLowerCase();&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1766560517566&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const payload = {
  email: normalizeEmail(email),
  password,
};&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프론트엔드와 백엔드 역할 분리&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 76px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 21px;&quot;&gt;구분&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 21px;&quot;&gt;프론트엔드&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 21px;&quot;&gt;백엔드&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 21px;&quot;&gt;이메일&amp;nbsp;소문자&amp;nbsp;처리&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 21px;&quot;&gt;전처리&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 21px;&quot;&gt;최종 검증&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;중복&amp;nbsp;계정&amp;nbsp;방지&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;UNIQUE 제약&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;보안&amp;nbsp;책임&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;X&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; height: 17px;&quot;&gt;O&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드는 UX 개선이 목적이고, 백엔드는 데이터 무결성 보장이 목적이다.&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;h3 data-ke-size=&quot;size23&quot;&gt;주의해야 할 포인트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이메일을&amp;nbsp;사용자&amp;nbsp;ID로&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;통일한다는&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;위한&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;형태의&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;/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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&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;대소문자&amp;nbsp;처리는&amp;nbsp;이론적인&amp;nbsp;표준보다&amp;nbsp;실무&amp;nbsp;기준을&amp;nbsp;우선하는&amp;nbsp;것이&amp;nbsp;중요하다.&amp;nbsp;RFC&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;로직은&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;이메일을&amp;nbsp;입력값&amp;nbsp;그대로&amp;nbsp;신뢰하기보다,&amp;nbsp;항상&amp;nbsp;소문자로&amp;nbsp;정규화(normalize)&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;회원가입에만&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;&amp;ldquo;가입은&amp;nbsp;됐는데&amp;nbsp;로그인이&amp;nbsp;안&amp;nbsp;되는&amp;rdquo;&amp;nbsp;문제와&amp;nbsp;같은&amp;nbsp;버그로&amp;nbsp;이어질&amp;nbsp;수&amp;nbsp;있다. &lt;br /&gt;&lt;br /&gt;마지막으로, 프론트엔드에서 이메일을 소문자로 처리하더라도 백엔드에서도 반드시 동일한 로직으로 다시 한 번 검증해야 한다. 프론트엔드는 사용자 경험과 입력 보정을 담당하고, 백엔드는 데이터 무결성과 보안을 책임진다. 이 두 영역이 같은 기준을 공유할 때, 안정적인 회원 시스템을 만들 수 있다.&lt;/p&gt;</description>
      <category>개발 지식모음/기타</category>
      <category>로그인</category>
      <category>이메일처리</category>
      <category>프론트엔드</category>
      <category>회원가입</category>
      <author>TTH</author>
      <guid isPermaLink="true">https://itconquest.tistory.com/201</guid>
      <comments>https://itconquest.tistory.com/entry/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90%EA%B0%80-%EC%95%8C%EC%95%84%EC%95%BC-%ED%95%A0-%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85-%EC%8B%9C-%EC%9D%B4%EB%A9%94%EC%9D%BC-%EB%8C%80%EC%86%8C%EB%AC%B8%EC%9E%90-%EC%B2%98%EB%A6%AC-%EA%B8%B0%EC%A4%80#entry201comment</comments>
      <pubDate>Wed, 24 Dec 2025 16:28:23 +0900</pubDate>
    </item>
    <item>
      <title>웹사이트 관리자라면 꼭 알아야 할 Google Admin Toolbox 기능 정리</title>
      <link>https://itconquest.tistory.com/entry/%EC%9B%B9%EC%82%AC%EC%9D%B4%ED%8A%B8-%EA%B4%80%EB%A6%AC%EC%9E%90%EB%9D%BC%EB%A9%B4-%EA%BC%AD-%EC%95%8C%EC%95%84%EC%95%BC-%ED%95%A0-Google-Admin-Toolbox-%EA%B8%B0%EB%8A%A5-%EC%A0%95%EB%A6%AC</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;구글 관리 콘솔 도구 상자란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구글 관리 콘솔 도구 상자(Google Admin Toolbox)는 Google에서 제공하는 네트워크&amp;middot;DNS&amp;middot;메일&amp;middot;도메인 관련 문제를 진단하기 위한 공식 기술 도구 모음이다. 개발자나 서버 관리자가 DNS 오류, 메일 전송 문제, SPF/DKIM/DMARC 설정, 도메인 소유권 문제 등을 해결할 때 아주 자주 사용하는 툴이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1159&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWkHsb/dJMcahQpPYs/K0Xo9KkeaJq3kdZGqmdaxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWkHsb/dJMcahQpPYs/K0Xo9KkeaJq3kdZGqmdaxk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWkHsb/dJMcahQpPYs/K0Xo9KkeaJq3kdZGqmdaxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWkHsb%2FdJMcahQpPYs%2FK0Xo9KkeaJq3kdZGqmdaxk%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;1159&quot; height=&quot;768&quot; data-origin-width=&quot;1159&quot; data-origin-height=&quot;768&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;&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;DIG, DNS 상태를 확인하는 도구&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DIG는 Google DNS 서버(8.8.8.8)를 기준으로 도메인의 DNS 정보를 실시간으로 조회할 수 있는 도구이다. &lt;br /&gt;DNS 관련 문제를 해결할 때 가장 많이 사용되는 핵심 도구이다.&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;b&gt;DIG로 확인할 수 있는 정보&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;A / AAAA 레코드&lt;/li&gt;
&lt;li&gt;CNAME 레코드&lt;/li&gt;
&lt;li&gt;TXT 레코드&lt;/li&gt;
&lt;li&gt;MX 레코드&lt;/li&gt;
&lt;li&gt;NS, SOA 정보&lt;/li&gt;
&lt;li&gt;DNS 전파 여부&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;&lt;b&gt;DIG가 필요한 상황&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;Search Console 소유권 인증이 실패할 때&lt;/li&gt;
&lt;li&gt;TXT 레코드가 제대로 들어갔는지 확인할 때&lt;/li&gt;
&lt;li&gt;DNS&amp;nbsp;설정이&amp;nbsp;정상적으로&amp;nbsp;전파되고&amp;nbsp;있는지&amp;nbsp;체크할&amp;nbsp;때&lt;/li&gt;
&lt;/ul&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-origin-width=&quot;1180&quot; data-origin-height=&quot;625&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brq6ya/dJMcabvTLUK/IUjHbVqR0y2iFjRZtVTwq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brq6ya/dJMcabvTLUK/IUjHbVqR0y2iFjRZtVTwq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brq6ya/dJMcabvTLUK/IUjHbVqR0y2iFjRZtVTwq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbrq6ya%2FdJMcabvTLUK%2FIUjHbVqR0y2iFjRZtVTwq0%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;1180&quot; height=&quot;625&quot; data-origin-width=&quot;1180&quot; data-origin-height=&quot;625&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;&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;Check MX, 이메일 설정 오류를 점검하는 도구&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Check MX는 도메인의 메일 시스템 구성을 검사해 주는 도구이다. &lt;br /&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;b&gt;Check MX로 확인할 수 있는 항목&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;MX 레코드 구성&lt;/li&gt;
&lt;li&gt;SPF 레코드 정상 여부&lt;/li&gt;
&lt;li&gt;DKIM 설정 여부&lt;/li&gt;
&lt;li&gt;DMARC 정책 여부&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1084&quot; data-origin-height=&quot;816&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rkX1n/dJMcaiuYQZn/4rWn10kKGoDema3pQEhSI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rkX1n/dJMcaiuYQZn/4rWn10kKGoDema3pQEhSI0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rkX1n/dJMcaiuYQZn/4rWn10kKGoDema3pQEhSI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrkX1n%2FdJMcaiuYQZn%2F4rWn10kKGoDema3pQEhSI0%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;1084&quot; height=&quot;816&quot; data-origin-width=&quot;1084&quot; data-origin-height=&quot;816&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;&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;Messageheader, 메일 전달 경로를 분석하는 도구&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Messageheader 도구는 이메일 헤더 정보를 분석하여 메일이 어떤 서버를 거쳐 전달되었는지, 인증은 제대로 되었는지 등을 보여준다.&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;확인 가능한 정보&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;SPF 통과 여부&lt;/li&gt;
&lt;li&gt;DKIM 서명 여부&lt;/li&gt;
&lt;li&gt;DMARC 적용 여부&lt;/li&gt;
&lt;li&gt;중간 SMTP 서버 경로&lt;/li&gt;
&lt;li&gt;지연된 지점 및 문제 발생 위치&lt;/li&gt;
&lt;li&gt;이메일이 스팸함으로 가거나 전달되지 않을 때 원인을 파악하는데 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1079&quot; data-origin-height=&quot;802&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJIv5m/dJMcagRvhn1/KYbknNcBYDffLNouv3IL21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJIv5m/dJMcagRvhn1/KYbknNcBYDffLNouv3IL21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJIv5m/dJMcagRvhn1/KYbknNcBYDffLNouv3IL21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJIv5m%2FdJMcagRvhn1%2FKYbknNcBYDffLNouv3IL21%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;1079&quot; height=&quot;802&quot; data-origin-width=&quot;1079&quot; data-origin-height=&quot;802&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;h3 data-ke-size=&quot;size23&quot;&gt;HAR Analyzer, 웹사이트 문제를 분석하는 도구&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HAR Analyzer는 브라우저에서 저장한 HAR(Network Log) 파일을 분석해 &lt;br /&gt;HTTP 요청 및 응답을 시각적으로 보여주는 도구이다.&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;HAR Analyzer가 유용한 상황&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;이미지&amp;middot;API 요청이 실패할 때&lt;/li&gt;
&lt;li&gt;CORS 문제나 인증 문제를 확인할 때&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1082&quot; data-origin-height=&quot;828&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGeWaF/dJMcafE4bQo/samw9eM8rPtWOl8pRnUMPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGeWaF/dJMcafE4bQo/samw9eM8rPtWOl8pRnUMPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGeWaF/dJMcafE4bQo/samw9eM8rPtWOl8pRnUMPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGeWaF%2FdJMcafE4bQo%2Fsamw9eM8rPtWOl8pRnUMPk%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;1082&quot; height=&quot;828&quot; data-origin-width=&quot;1082&quot; data-origin-height=&quot;828&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;마치며&lt;/h3&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;정리하면 구글 관리 콘솔 도구 상자는 DNS 문제, 이메일 문제, 도메인 인증 문제를 해결하기 위해 반드시 알아야 하는 필수 진단 도구 모음이다. DNS가 잘못됐는지 확인하고 싶다면 DIG, 이메일 발송&amp;middot;수신 문제가 있다면 Check MX, 메일 인증이나 경로 문제를 분석하고 싶다면 Messageheader, 웹사이트 네트워크 오류를 분석하고 싶다면 HAR Analyzer, 이렇게&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;&lt;/p&gt;</description>
      <category>구글&amp;amp;SEO</category>
      <category>Admin Toolbox</category>
      <category>구글관리콘솔도구상자</category>
      <author>TTH</author>
      <guid isPermaLink="true">https://itconquest.tistory.com/200</guid>
      <comments>https://itconquest.tistory.com/entry/%EC%9B%B9%EC%82%AC%EC%9D%B4%ED%8A%B8-%EA%B4%80%EB%A6%AC%EC%9E%90%EB%9D%BC%EB%A9%B4-%EA%BC%AD-%EC%95%8C%EC%95%84%EC%95%BC-%ED%95%A0-Google-Admin-Toolbox-%EA%B8%B0%EB%8A%A5-%EC%A0%95%EB%A6%AC#entry200comment</comments>
      <pubDate>Sun, 14 Dec 2025 21:50:16 +0900</pubDate>
    </item>
    <item>
      <title>React Server Components(RSC) 보안 취약점 발견 및 공식 패치 발표 정리 (2025)</title>
      <link>https://itconquest.tistory.com/entry/React-Server-Components-RSC-%EB%B3%B4%EC%95%88-%EC%B7%A8%EC%95%BD%EC%A0%90-%EB%B0%9C%EA%B2%AC-%EB%B0%8F-%EA%B3%B5%EC%8B%9D-%ED%8C%A8%EC%B9%98-%EB%B0%9C%ED%91%9C-%EC%A0%95%EB%A6%AC-2025</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;2025년 12월, React 생태계에 큰 영향을 미치는 중대 보안 취약점이 공개되었다. 특히 React Server Components(RSC)를 사용하는 프로젝트에서 인증 없이 원격 코드 실행(Remote Code Execution, RCE)이 가능해질 수 있는 치명적인 문제였다. &lt;br /&gt;&lt;br /&gt;React 팀은 즉시 패치를 배포했고, 전 세계 개발자들에게 빠른 업데이트를 권고하고 있다. 이 글에서는 해당 취약점의 원인, 영향 범위, 해결 방법을 쉽게 이해할 수 있도록 정리해보았다.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;822&quot; data-origin-height=&quot;279&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cloxPq/dJMcahCVhvi/hG6ipsRwDtigrzNidLAnW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cloxPq/dJMcahCVhvi/hG6ipsRwDtigrzNidLAnW0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cloxPq/dJMcahCVhvi/hG6ipsRwDtigrzNidLAnW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcloxPq%2FdJMcahCVhvi%2FhG6ipsRwDtigrzNidLAnW0%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;822&quot; height=&quot;279&quot; data-origin-width=&quot;822&quot; data-origin-height=&quot;279&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;RSC에서 어떤 취약점이 발생했나?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 RSC가 서버와 클라이언트 사이에서 데이터를 교환할 때 사용하는 &amp;ldquo;Flight 프로토콜&amp;rdquo;의 페이로드 검증 부족이 원인이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 결과 인증 없이도 공격자가 서버에 악의적인 요청을 보낼 수 있고,&amp;nbsp; 그 요청이 서버에서 바로 **임의의 JavaScript 코드 실행(RCE)**로 이어질 수 있었다.&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;&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;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 취약점은 CVSS 10.0점(최고 위험 등급)을 받을 만큼 심각한 문제로, 단순한 정보 유출을 넘어 서버 자체가 공격자에게 장악될 수 있는 수준이며, 일부 보안업체는 클라우드 환경의 약 40%가 잠재적으로 영향권에 있다고 분석하고 있다. 또한 이미 공개된 개념증명(PoC)을 바탕으로 실제 공격 시도가 보고되고 있는 상황이어서, 즉각적인 패치 적용이 필수적이다.&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;h3 data-ke-size=&quot;size23&quot;&gt;어떤 환경이 영향을 받았나?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Server Components(RSC) 기반 패키지인 react-server-dom-webpack, react-server-dom-parcel, react-server-dom-turbopack의 19.0.0, 19.1.0&amp;ndash;19.1.1, 19.2.0 버전에서 심각한 보안 취약점이 발견되었으며, RSC를 사용하는 Next.js 14.x(카나리 포함)와 Next.js 15.x&amp;middot;16.x의 일부 버전 또한 동일한 취약점의 영향을 받은 것으로 확인되었다. 따라서 서버 사이드 렌더링 구조를 사용하는 프로젝트, 특히 App Router 기반의 Next.js 프로젝트는 대부분 위험에 노출되었을 가능성이 높아, 즉각적인 버전 점검과 업데이트가 필요하다.&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;다음 취약한 버전으로 분류된 React RSC 패키지들은 다음과 같다.&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;취약 버전&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react-server-dom-webpack&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react-server-dom-parcel&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react-server-dom-turbopack &lt;br /&gt;&amp;rarr;&amp;nbsp;19.0.0,&amp;nbsp;19.1.0&amp;ndash;19.1.1,&amp;nbsp;19.2.0 &lt;br /&gt;&lt;br /&gt;&lt;b&gt;RSC 기반으로 동작&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js 14.x (Canary 포함)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js 15.x / 16.x 일부 버전&lt;br /&gt;&lt;br /&gt;따라서 App Router를 사용하는 Next.js 프로젝트는 대부분 위험에 노출되었을 가능성이 있다.&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;h3 data-ke-size=&quot;size23&quot;&gt;React 팀이 발표한 보안 패치 내용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React 팀은 이번 취약점을 공개함과 동시에 react-server-dom-* 패키지의 19.0.1, 19.1.2, 19.2.1 버전을 포함한 보안 패치를 즉시 배포했으며, RSC를 사용하는 Next.js 역시 취약 버전 사용자를 대상으로 최신 안정 버전으로 즉시 업데이트할 것을 강력히 권고했다. 또한 단순히 패키지만 업데이트하는 것으로는 충분하지 않기 때문에, 업데이트 후 반드시 프로젝트를 다시 빌드하고 재배포해야 패치가 실제로 적용되어 보안 위험을 완전히 해소할 수 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;react-server-dom-* 패키지:&lt;br /&gt;&amp;rarr;&amp;nbsp;19.0.1,&amp;nbsp;19.1.2,&amp;nbsp;19.2.1&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;패치된 안전 버전&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;react-server-dom-* 패키지:&lt;br /&gt;&amp;rarr;&amp;nbsp;19.0.1,&amp;nbsp;19.1.2,&amp;nbsp;19.2.1&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;Next.js&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&amp;rarr; 취약 버전 사용자에게 최신 안정 버전으로 즉시 업데이트 권고&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;단순히 패키지를 업데이트하는 것 뿐만 아니라, 업데이트 후 반드시 빌드와 재배포를 다시 해야 패치가 실제로 적용된다.&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;h3 data-ke-size=&quot;size23&quot;&gt;패치 적용이 어려울 때의 임시 대응책&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 즉시 업데이트가 어려운 경우에는 RSC 관련 엔드포인트(예: /_flight)에 대한 접근을 제한하거나, WAF(웹 방화벽)를 이용해 비정상적인 Flight 요청을 차단하고, 미들웨어에서 페이로드 구조를 강화하는 방식으로 위험을 최소화할 수 있지만, 이러한 방법들은 어디까지나 임시 대응에 불과하며 근본적인 해결책은 보안 패치를 적용하고 시스템을 재빌드&amp;middot;재배포하는 것이라는 점을 반드시 고려해야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RSC 관련 엔드포인트(예: /_flight)에 대한 접근 제한&lt;/li&gt;
&lt;li&gt;WAF(웹 방화벽)에서 비정상적인 Flight 요청 차단&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;&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;h3 data-ke-size=&quot;size23&quot;&gt;개발자가 반드시 해야 할 것&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자는&amp;nbsp;이번&amp;nbsp;취약점에&amp;nbsp;대응하기&amp;nbsp;위해&amp;nbsp;React&amp;nbsp;및&amp;nbsp;Next.js&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;Flight&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;/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;1. React 및 Next.js 패키지를 최신 버전으로 업데이트&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 프로젝트&amp;nbsp;재빌드&amp;nbsp;&amp;rarr;&amp;nbsp;재배포 &lt;/b&gt;&lt;br /&gt;&lt;b&gt;3. 배포 후 서버 로그 확인, 비정상 Flight 요청 모니터링&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. 보안 공지 구독 또는 자동 보안 업데이트 설정&lt;/b&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;&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;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 이슈는 RSC를 기반으로 한 최신 웹 개발 방식이 생각보다 복잡한 보안 구조 위에 있다는 사실을 다시 한번 보여준 사건이었다. React 팀이 빠르게 대응해 패치를 내놓으면서 큰 문제는 막을 수 있었지만, 실제 프로젝트를 운영하는 개발자 입장에서는 직접 업데이트를 적용하고 점검하는 일이 가장 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;만약 React Server Components나 Next.js App Router를 사용하고 있다면, 지금 한 번 버전과 설정을 점검해보는 것이 좋을 듯 싶다. 작은 업데이트 하나가 전체 서비스의 안전을 지켜줄 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&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;a href=&quot;https://www.jeonguknews.co.kr/news/articleView.html?idxno=80495&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;CISA,&amp;nbsp;치명적&amp;nbsp;React&amp;nbsp;Server&amp;nbsp;Components&amp;nbsp;취약점&amp;nbsp;긴급&amp;nbsp;경보&amp;hellip;&amp;nbsp;&amp;ldquo;비인증&amp;nbsp;원격&amp;nbsp;코드&amp;nbsp;실행,&amp;nbsp;이미&amp;nbsp;활발히&amp;nbsp;악용&amp;nbsp;중&amp;rdquo;&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.boannews.com/media/view.asp?idx=140819&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;리액트&amp;nbsp;취약점&amp;nbsp;악용&amp;nbsp;급증...&amp;nbsp;KISA&amp;middot;보안업계&amp;nbsp;긴급&amp;nbsp;대응&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>개발 지식모음/ReactJS&amp;amp;TypeScript</category>
      <category>CVEs</category>
      <category>next.js</category>
      <category>React 보안</category>
      <category>RSC</category>
      <category>보안경고</category>
      <category>보안취약점</category>
      <category>웹보안</category>
      <author>TTH</author>
      <guid isPermaLink="true">https://itconquest.tistory.com/199</guid>
      <comments>https://itconquest.tistory.com/entry/React-Server-Components-RSC-%EB%B3%B4%EC%95%88-%EC%B7%A8%EC%95%BD%EC%A0%90-%EB%B0%9C%EA%B2%AC-%EB%B0%8F-%EA%B3%B5%EC%8B%9D-%ED%8C%A8%EC%B9%98-%EB%B0%9C%ED%91%9C-%EC%A0%95%EB%A6%AC-2025#entry199comment</comments>
      <pubDate>Tue, 9 Dec 2025 15:27:22 +0900</pubDate>
    </item>
    <item>
      <title>프론트(React)에서 안전하게 AWS S3에 직접 업로드하기, Presigned URL</title>
      <link>https://itconquest.tistory.com/entry/%ED%94%84%EB%A1%A0%ED%8A%B8React%EC%97%90%EC%84%9C-%EC%95%88%EC%A0%84%ED%95%98%EA%B2%8C-AWS-S3%EC%97%90-%EC%A7%81%EC%A0%91-%EC%97%85%EB%A1%9C%EB%93%9C%ED%95%98%EA%B8%B0-Presigned-URL</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Presigned URL을 사용하는 이유&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인&amp;nbsp;파일&amp;nbsp;업로드&amp;nbsp;방식은&amp;nbsp;프론트에서&amp;nbsp;백엔드로&amp;nbsp;파일을&amp;nbsp;보내고,&amp;nbsp;백엔드가&amp;nbsp;다시&amp;nbsp;S3로&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;Presigned&amp;nbsp;URL&amp;nbsp;방식은&amp;nbsp;프론트가&amp;nbsp;백엔드에&amp;nbsp;업로드용&amp;nbsp;URL&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;URL로&amp;nbsp;직접&amp;nbsp;S3에&amp;nbsp;업로드하기&amp;nbsp;때문에&amp;nbsp;백엔드&amp;nbsp;부하&amp;nbsp;없이&amp;nbsp;더&amp;nbsp;빠르고&amp;nbsp;안정적인&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;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;일반적인 업로드 방식&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Presigned URL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;1. 프론트가 백엔드로 파일 전송&lt;br /&gt;2. 백엔드가 S3로 파일 어볼드&lt;br /&gt;3. 업로드 성공 후 URL 반환&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;1. 프론트가 백엔드에 업로드 URL 생성 요청&lt;br /&gt;2. 백엔드가 Presigned URL 반환&lt;br /&gt;3. 프론트가 S3에 직접 업로드&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;&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;Presigned URL 업로드의 기본 원리&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt; &amp;ldquo;S3가&amp;nbsp;업로드를&amp;nbsp;허용하는&amp;nbsp;비밀&amp;nbsp;링크를&amp;nbsp;백엔드에서&amp;nbsp;생성하고&amp;nbsp;프론트에&amp;nbsp;던져주는&amp;nbsp;것&amp;rdquo;&lt;/span&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;Presigned&amp;nbsp;URL은&amp;nbsp;S3가&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;URL로&amp;nbsp;직접&amp;nbsp;PUT&amp;nbsp;요청을&amp;nbsp;보내&amp;nbsp;S3에&amp;nbsp;곧바로&amp;nbsp;파일을&amp;nbsp;저장할&amp;nbsp;수&amp;nbsp;있게&amp;nbsp;해주는&amp;nbsp;구조이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;해당 URL은 일정 시간 동안만 유효하며, 이 URL로 PUT 요청을 보내면 곧바로 S3에 저장된다.&lt;br /&gt;&lt;br /&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;단일 파일 업로드 구현 (프론트 코드 예시)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단일&amp;nbsp;파일&amp;nbsp;업로드의&amp;nbsp;흐름은&amp;nbsp;아래와&amp;nbsp;같다. &lt;br /&gt;&lt;br /&gt;1. 서버에 presigned URL 발급 요청 &lt;br /&gt;2. 서버가&amp;nbsp;URL&amp;nbsp;반환 &lt;br /&gt;3. 해당&amp;nbsp;URL로&amp;nbsp;PUT&amp;nbsp;요청하여&amp;nbsp;파일&amp;nbsp;업로드 &lt;br /&gt;4. 업로드된 S3 경로 반환&lt;/p&gt;
&lt;pre id=&quot;code_1763428935618&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async function uploadToS3UsingPresignedURL(file) {
  const res = await fetch(&quot;https://api.softlanders.com/api/test-s3-upload4&quot;, {
    method: &quot;POST&quot;,
    headers: {&quot;Content-Type&quot;: &quot;application/json&quot;},
    body: JSON.stringify({
      filename: file.name,
      folder: &quot;test_folder&quot;,
      content_type: file.type
    })
  });

  const result = await res.json();
  if (result.status !== &quot;success&quot;) {
    console.error(&quot;Presigned URL 생성 실패:&quot;, result.error);
    return;
  }

  const { upload_url, s3_key } = result;

  const uploadRes = await fetch(upload_url, {
    method: &quot;PUT&quot;,
    headers: {
      &quot;Content-Type&quot;: file.type
    },
    body: file
  });

  if (uploadRes.ok) {
    console.log(`업로드 성공! S3 경로: ${s3_key}`);
  } else {
    console.error(&quot;S3 업로드 실패:&quot;, uploadRes.status);
  }
}&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;다중 파일 업로드 구조 설계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다중 파일 업로드의 핵심은 프론트에서 파일 자체가 아니라 파일 이름 목록만 백엔드로 전달하면, 백엔드가 이 이름들을 기준으로 여러 개의 Presigned URL을 생성해 배열 형태로 반환하고, 프론트는 해당 배열을 순회하면서 각 URL로 파일을 직접 S3에 업로드하는 구조라는 점이다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 59px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 21px;&quot;&gt;프론트&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 21px;&quot;&gt;백엔드&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 21px;&quot;&gt;백엔드로 파일 이름 목록만 전달&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 21px;&quot;&gt;프론트로 URL 배열 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;백엔드는 이 이름 기준으로 Presigned URL을 여러 개 발급한다.&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;프론트는&amp;nbsp;이&amp;nbsp;배열을&amp;nbsp;돌면서&amp;nbsp;각각&amp;nbsp;S3&amp;nbsp;업로드를&amp;nbsp;실행한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&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;&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;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1차 요청, presigned URL 요청&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1763429030943&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const res = await fetch(&quot;https://api.softlanders.com/api/admin_dashboard_quote_write&quot;, {
  method: &quot;POST&quot;,
  headers: { &quot;Content-Type&quot;: &quot;application/json&quot; },
  body: JSON.stringify({
    ID1: id1,
    ID2: id2,
    business_register_number: brn,
    currency: currency,
    quotes: JSON.stringify(quotes),
    quote_description: desc,
    files: files.map(f =&amp;gt; f.name) // 파일 이름만 전송
  })
});&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;&lt;b&gt;2차 요청, Presigned URL에 실제 업로드&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1763429046850&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for (const { upload_url, s3_key } of result.presigned_urls) {
  const file = files.find(f =&amp;gt; s3_key.endsWith(f.name));
  const uploadRes = await fetch(upload_url, {
    method: &quot;PUT&quot;,
    body: file
  });

  if (!uploadRes.ok) {
    console.error(&quot;S3 업로드 실패:&quot;, s3_key);
  } else {
    console.log(&quot;업로드 성공:&quot;, s3_key);
  }
}&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;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제 1: PUT 요청에 Content-Type 헤더를 넣으면 실패함&lt;/b&gt; &lt;br /&gt;AWS S3 Presigned URL 중 v4 방식은 Content-Type이 사전에 지정된 경우에만 허용된다. &lt;br /&gt;서버에서&amp;nbsp;Content-Type을&amp;nbsp;지정하지&amp;nbsp;않은&amp;nbsp;상태라면&amp;nbsp;프론트에서&amp;nbsp;넣지&amp;nbsp;말아야&amp;nbsp;한다. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;문제 2: 파일 이름이 겹치면 덮어쓰임&lt;/b&gt; &lt;br /&gt;파일 이름을 통일해 저장하면 충돌이 발생한다. &lt;br /&gt;그래서 보통은 백엔드에서 다음과 같은 방식을 사용한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;UUID + 원본 파일명&lt;/li&gt;
&lt;li&gt;timestamp + filename&lt;/li&gt;
&lt;li&gt;사용자 ID 기반 디렉토리 분리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제 3: CORS 오류&lt;/b&gt; &lt;br /&gt;S3 버킷 CORS 설정에 반드시 아래 항목이 있어야 한다&lt;/p&gt;
&lt;pre id=&quot;code_1763429340778&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PUT
Content-Type&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;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Presigned URL 방식은 프론트와 백엔드 모두에게 이점이 많다. 특히 다중 파일 업로드에서는 이 방식이 거의 정석이다.&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;업로드 속도 향상&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;프로젝트를 진행하면서 느낀 점은, Presigned URL 구조만 잘 설계해두면 나머지는 매우 단순하고 직관적으로 구현된다는 점이다. &lt;br /&gt;앞으로&amp;nbsp;대용량&amp;nbsp;파일&amp;nbsp;업로드가&amp;nbsp;필요한&amp;nbsp;다른&amp;nbsp;기능에도&amp;nbsp;적극적으로&amp;nbsp;적용해&amp;nbsp;볼&amp;nbsp;생각이다.&lt;/p&gt;</description>
      <category>개발 지식모음/AWS</category>
      <category>AWS</category>
      <category>Presigned URL</category>
      <category>S3</category>
      <author>TTH</author>
      <guid isPermaLink="true">https://itconquest.tistory.com/198</guid>
      <comments>https://itconquest.tistory.com/entry/%ED%94%84%EB%A1%A0%ED%8A%B8React%EC%97%90%EC%84%9C-%EC%95%88%EC%A0%84%ED%95%98%EA%B2%8C-AWS-S3%EC%97%90-%EC%A7%81%EC%A0%91-%EC%97%85%EB%A1%9C%EB%93%9C%ED%95%98%EA%B8%B0-Presigned-URL#entry198comment</comments>
      <pubDate>Tue, 18 Nov 2025 10:32:53 +0900</pubDate>
    </item>
    <item>
      <title>프론트엔드 개발자가 업무할 때 자주 쓰는 자바스크립트 내장 함수 10개</title>
      <link>https://itconquest.tistory.com/entry/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90%EA%B0%80-%EC%97%85%EB%AC%B4%ED%95%A0-%EB%95%8C-%EC%9E%90%EC%A3%BC-%EC%93%B0%EB%8A%94-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%82%B4%EC%9E%A5-%ED%95%A8%EC%88%98-10%EA%B0%9C</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드 개발을 하다 보면 수많은 내장 함수를 마주하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그중에서도 업무에서 자주 쓰이고, 꼭 알아두면 유용한 함수 10가지를 정리해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 함수들은 React, Vue, 바닐라 JS 어디에서든 자주 등장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;console.log()&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 기본적이지만, 가장 많이 쓰이는 함수이다.&lt;br /&gt;디버깅을 하거나 값이 제대로 들어왔는지 확인할 때 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1762325680425&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log('현재 상태:', state);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;445&quot; data-start=&quot;414&quot; data-ke-size=&quot;size16&quot;&gt;코드 중간중간에 로그를 찍어두면 문제를 찾기 훨씬 쉽다.&lt;/p&gt;
&lt;p data-end=&quot;445&quot; data-start=&quot;414&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;445&quot; data-start=&quot;414&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;445&quot; data-start=&quot;414&quot; data-ke-size=&quot;size23&quot;&gt;document.querySelector()&lt;/h3&gt;
&lt;p data-end=&quot;445&quot; data-start=&quot;414&quot; data-ke-size=&quot;size16&quot;&gt;HTML 요소를 선택할 때 사용하는 함수이다.&lt;/p&gt;
&lt;p data-end=&quot;445&quot; data-start=&quot;414&quot; data-ke-size=&quot;size16&quot;&gt;CSS 선택자 문법을 그대로 쓸 수 있어서 직관적이다.&lt;/p&gt;
&lt;pre id=&quot;code_1762325704148&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const button = document.querySelector('#submitBtn');
button.addEventListener('click', () =&amp;gt; alert('제출 완료!'));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;445&quot; data-start=&quot;414&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;445&quot; data-start=&quot;414&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;getElementById()보다 더 유연하게 쓸 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;445&quot; data-start=&quot;414&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;445&quot; data-start=&quot;414&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;445&quot; data-start=&quot;414&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;445&quot; data-start=&quot;414&quot; data-ke-size=&quot;size23&quot;&gt;addEventListener()&lt;/h3&gt;
&lt;p data-end=&quot;445&quot; data-start=&quot;414&quot; data-ke-size=&quot;size16&quot;&gt;이벤트를 등록할 때 쓰는 함수이다.&lt;br /&gt;버튼 클릭, 입력 변화, 스크롤 등 다양한 이벤트를 감지할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1762325892573&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;window.addEventListener('scroll', () =&amp;gt; console.log('스크롤 중'));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;445&quot; data-start=&quot;414&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;445&quot; data-start=&quot;414&quot; data-ke-size=&quot;size16&quot;&gt;프론트엔드 인터랙션을 구현할 때 자주 사용된다.&lt;/p&gt;
&lt;p data-end=&quot;445&quot; data-start=&quot;414&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;445&quot; data-start=&quot;414&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;445&quot; data-start=&quot;414&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;445&quot; data-start=&quot;414&quot; data-ke-size=&quot;size23&quot;&gt;setTimeout() / setInterval()&lt;/h3&gt;
&lt;p data-end=&quot;988&quot; data-start=&quot;941&quot; data-ke-size=&quot;size16&quot;&gt;시간 관련 함수로, 일정 시간 뒤 실행하거나 반복 실행할 때 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1762325908830&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;setTimeout(() =&amp;gt; console.log('3초 후 실행'), 3000);
setInterval(() =&amp;gt; console.log('반복 실행'), 1000);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;988&quot; data-start=&quot;941&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;988&quot; data-start=&quot;941&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;애니메이션, 알림, 자동 갱신 기능 등에 자주 사용된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;1127&quot; data-start=&quot;1096&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1127&quot; data-start=&quot;1096&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1127&quot; data-start=&quot;1096&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;1127&quot; data-start=&quot;1096&quot; data-ke-size=&quot;size23&quot;&gt;JSON.stringify() / JSON.parse()&lt;/h3&gt;
&lt;p data-end=&quot;1127&quot; data-start=&quot;1096&quot; data-ke-size=&quot;size16&quot;&gt;객체와 JSON 문자열 간 변환을 할 때 사용하는 함수이다.&lt;br /&gt;서버 통신이나 localStorage 저장 시 반드시 거쳐가는 과정이다.&lt;/p&gt;
&lt;pre id=&quot;code_1762325922664&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const user = { name: '민준', age: 30 };
localStorage.setItem('user', JSON.stringify(user));
const storedUser = JSON.parse(localStorage.getItem('user'));&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;프론트엔드 데이터 관리의 기본 중 기본이다.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;fetch()&lt;/span&gt;&lt;/h3&gt;
&lt;div&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;서버와 통신할 때 사용하는 함수이다.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;GET, POST 등 HTTP 요청을 보낼 수 있으며, 비동기 처리에 자주 쓰인다.&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1762325972296&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fetch('/api/data') .then(res =&amp;gt; res.json()) .then(data =&amp;gt; console.log(data));&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1668&quot; data-start=&quot;1628&quot; data-ke-size=&quot;size16&quot;&gt;React나 Vue에서 데이터를 불러올 때 거의 항상 등장하는 함수이다.&lt;/p&gt;
&lt;p data-end=&quot;1668&quot; data-start=&quot;1628&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1668&quot; data-start=&quot;1628&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1668&quot; data-start=&quot;1628&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;1668&quot; data-start=&quot;1628&quot; data-ke-size=&quot;size23&quot;&gt;localStorage / sessionStorage&lt;/h3&gt;
&lt;p data-end=&quot;1778&quot; data-start=&quot;1713&quot; data-ke-size=&quot;size16&quot;&gt;브라우저에 데이터를 저장할 때 쓰는 객체이다.&lt;br /&gt;토큰, 로그인 정보, 사용자 설정값 등을 유지할 때 자주 사용된다.&lt;/p&gt;
&lt;pre id=&quot;code_1762325985282&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;localStorage.setItem('theme', 'dark'); const theme = localStorage.getItem('theme');&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;1778&quot; data-start=&quot;1713&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1938&quot; data-start=&quot;1875&quot; data-ke-size=&quot;size16&quot;&gt;localStorage는 브라우저를 꺼도 유지되고, sessionStorage는 탭을 닫으면 사라진다.&lt;/p&gt;
&lt;p data-end=&quot;1938&quot; data-start=&quot;1875&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1938&quot; data-start=&quot;1875&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;1938&quot; data-start=&quot;1875&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;1938&quot; data-start=&quot;1875&quot; data-ke-size=&quot;size23&quot;&gt;Array.map() / filter() / forEach()&lt;/h3&gt;
&lt;p data-end=&quot;1938&quot; data-start=&quot;1875&quot; data-ke-size=&quot;size16&quot;&gt;배열을 다룰 때 가장 자주 쓰이는 내장 함수들이다.&lt;br /&gt;리스트 데이터를 렌더링하거나 가공할 때 필수적이다.&lt;/p&gt;
&lt;pre id=&quot;code_1762326023712&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const users = ['준혁', '민준', '아라']; 
users.map(name =&amp;gt; console.log(name)); // 각각 출력 
const filtered = users.filter(name =&amp;gt; name !== '민준');&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;span style=&quot;letter-spacing: 0px;&quot;&gt;특히 React에서 .map()은 JSX 반복 렌더링에 자주 사용된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;2237&quot; data-start=&quot;2196&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2237&quot; data-start=&quot;2196&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2237&quot; data-start=&quot;2196&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;2237&quot; data-start=&quot;2196&quot; data-ke-size=&quot;size23&quot;&gt;Object.keys() / Object.entries()&lt;/h3&gt;
&lt;p data-end=&quot;2237&quot; data-start=&quot;2196&quot; data-ke-size=&quot;size16&quot;&gt;객체의 키나 값, 혹은 전체를 순회할 때 사용된다.&lt;/p&gt;
&lt;pre id=&quot;code_1762326062632&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const user = { name: '세희', age: 30 }; 
console.log(Object.keys(user)); // ['name', 'age'] 
console.log(Object.entries(user)); // [['name', '세희'], ['age', 30]]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;2237&quot; data-start=&quot;2196&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2237&quot; data-start=&quot;2196&quot; data-ke-size=&quot;size16&quot;&gt;API 응답으로 받은 데이터를 다룰 때 유용하다.&lt;/p&gt;
&lt;p data-end=&quot;2513&quot; data-start=&quot;2486&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2513&quot; data-start=&quot;2486&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2513&quot; data-start=&quot;2486&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-end=&quot;2513&quot; data-start=&quot;2486&quot; data-ke-size=&quot;size23&quot;&gt;Math.random() / Math.floor()&lt;/h3&gt;
&lt;p data-end=&quot;2513&quot; data-start=&quot;2486&quot; data-ke-size=&quot;size16&quot;&gt;랜덤 값 생성이나 반올림, 올림, 내림 계산에 사용된다.&lt;/p&gt;
&lt;pre id=&quot;code_1762326082023&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const randomNum = Math.floor(Math.random() * 10);
console.log(randomNum); // 0~9 사이의 정수&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;2513&quot; data-start=&quot;2486&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2513&quot; data-start=&quot;2486&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;게임, 추첨, 랜덤 추천 등 다양한 기능에 쓰인다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;2716&quot; data-start=&quot;2688&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2716&quot; data-start=&quot;2688&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-end=&quot;2716&quot; data-start=&quot;2688&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2716&quot; data-start=&quot;2688&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;2716&quot; data-start=&quot;2688&quot; data-ke-size=&quot;size16&quot;&gt;이 10가지 내장 함수는 프론트엔드 개발에서 거의 매일 쓰인다고 해도 과언이 아니다.&lt;br /&gt;단순히 문법을 외우기보다는, &amp;ldquo;어떤 상황에서 어떤 함수를 쓰면 효율적인가&amp;rdquo;를 이해하는 것이 중요한 것 같다.&lt;/p&gt;
&lt;p data-end=&quot;2847&quot; data-start=&quot;2733&quot; data-ke-size=&quot;size16&quot;&gt;이 함수들만 잘 다뤄도 프론트엔드 코드의 70% 이상은 훨씬 깔끔해진다. 기본기지만, 결국 가장 강력한 무기이다.&lt;/p&gt;</description>
      <category>개발 지식모음/JavaScript</category>
      <category>내장함수</category>
      <category>자바스크립트 함수</category>
      <author>TTH</author>
      <guid isPermaLink="true">https://itconquest.tistory.com/197</guid>
      <comments>https://itconquest.tistory.com/entry/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90%EA%B0%80-%EC%97%85%EB%AC%B4%ED%95%A0-%EB%95%8C-%EC%9E%90%EC%A3%BC-%EC%93%B0%EB%8A%94-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%82%B4%EC%9E%A5-%ED%95%A8%EC%88%98-10%EA%B0%9C#entry197comment</comments>
      <pubDate>Wed, 5 Nov 2025 16:03:52 +0900</pubDate>
    </item>
  </channel>
</rss>