<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Notebase</title>
    <link>https://notebase.tistory.com/</link>
    <description>AI, 개발 등 다양한 분야에 대한 지식을 공부하면서 기록하는 블로그입니다.</description>
    <language>ko</language>
    <pubDate>Wed, 10 Jun 2026 21:48:57 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>notebase</managingEditor>
    <image>
      <title>Notebase</title>
      <url>https://tistory1.daumcdn.net/tistory/8694161/attach/67fb8c71c26d47cdb67c4628ad303249</url>
      <link>https://notebase.tistory.com</link>
    </image>
    <item>
      <title>AGENTS.md란? Codex&amp;middot;Claude Code&amp;middot;Cursor에서 프로젝트 지침 파일을 쓰는 이유</title>
      <link>https://notebase.tistory.com/entry/agents-md-codex-claude-code-cursor-rules</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;AGENTS.md가 무엇인지, Codex&amp;middot;Claude Code&amp;middot;Cursor에서 프로젝트 지침 파일을 어떻게 쓰는지 정리합니다. Claude Code의 CLAUDE.md, Cursor Rules와 .mdc 파일 구조, 실제 작성 예시와 주의사항까지 초보자 기준으로 설명합니다.&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;code&gt;AGENTS.md&lt;/code&gt;는 AI 코딩 에이전트에게 &amp;ldquo;이 프로젝트에서는 이렇게 작업해라&amp;rdquo;라고 알려주는 지침 파일이다. Codex를 쓰다 보면 자주 보이고, Claude Code나 Cursor까지 같이 쓰면 파일명이 달라서 헷갈리기 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 결론부터 정리하면 이렇다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;도구&lt;/th&gt;
&lt;th&gt;기본 프로젝트 지침 파일&lt;/th&gt;
&lt;th&gt;동작 방식&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Codex&lt;/td&gt;
&lt;td&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;작업 전 프로젝트 지침으로 참고&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Code&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Claude Code 메모리/프로젝트 지침으로 참고&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cursor&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.cursor/rules&lt;/code&gt;, Cursor Rules, &lt;code&gt;AGENTS.md&lt;/code&gt; 관련 지원&lt;/td&gt;
&lt;td&gt;규칙 파일과 범위별 Rules를 통해 적용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;공통 목적&lt;/td&gt;
&lt;td&gt;프로젝트 구조, 명령어, 코드 스타일, 작업 규칙 전달&lt;/td&gt;
&lt;td&gt;AI가 작업 전 참고할 기준 제공&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;여기서 중요한 점은 **모든 AI 코딩 도구가 &lt;code&gt;AGENTS.md&lt;/code&gt; 하나만 공통으로 읽는 것은 아니라는 점**이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 Claude Code는 &lt;code&gt;AGENTS.md&lt;/code&gt;가 아니라 &lt;code&gt;CLAUDE.md&lt;/code&gt;가 기본이다. 2026년 6월 기준 Anthropic 공식 문서에서는 Claude Code의 프로젝트 메모리 파일로 &lt;code&gt;CLAUDE.md&lt;/code&gt;를 안내한다. 이미 &lt;code&gt;AGENTS.md&lt;/code&gt;를 쓰는 프로젝트라면 &lt;code&gt;CLAUDE.md&lt;/code&gt; 안에서 &lt;code&gt;@AGENTS.md&lt;/code&gt;로 가져오거나, 심볼릭 링크를 만들어 공통 지침을 공유하는 식으로 구성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;AGENTS.md는 AI 코딩 에이전트용 프로젝트 설명서다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자가 새 프로젝트에 들어오면 보통 &lt;code&gt;README.md&lt;/code&gt;를 먼저 읽는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 에이전트도 비슷하다. 다만 AI에게는 사람이 읽는 설명서보다 조금 더 직접적인 지시가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 이런 내용이다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;- 패키지 매니저는 npm이 아니라 pnpm을 사용한다.
- 테스트는 변경 후 pnpm test로 확인한다.
- API 라우터는 src/routes 아래에 둔다.
- DB 스키마를 직접 수정하지 말고 migration 파일을 만든다.
- 보안 관련 파일은 수정하기 전에 먼저 설명한다.&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;ldquo;이 프로젝트는 FastAPI야. SQLAlchemy 쓰고, 테스트는 pytest로 돌리고, env 파일은 건드리지 말고, 기존 구조 따라줘.&amp;rdquo;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 이렇게 말해도, 세션이 바뀌면 다시 설명해야 한다. 작업 도중에도 AI가 프로젝트 규칙을 잊거나, 다른 방식으로 코드를 짤 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;나 &lt;code&gt;CLAUDE.md&lt;/code&gt; 같은 파일은 이 반복 설명을 줄이기 위한 장치다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;왜 프로젝트 지침 파일이 필요한가&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 도구는 코드베이스를 읽을 수 있지만, 프로젝트의 암묵적인 규칙까지 항상 정확히 이해하는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 같은 React 프로젝트라도 팀마다 구조가 다르다.&lt;/p&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;src/components
src/features
src/app
src/pages
src/api&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;AI가 주변 코드를 보고 어느 정도 맞출 수는 있다. 하지만 프로젝트가 커질수록 애매한 선택지가 많아진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 지침 파일이 있으면 AI가 작업 전에 참고할 기준이 생긴다.&lt;/p&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;- 새 기능은 src/features/{featureName} 아래에 만든다.
- 공통 UI 컴포넌트는 src/components/ui에 둔다.
- API 호출 코드는 src/lib/api에 둔다.
- 테스트 파일은 원본 파일 옆에 *.test.ts 형식으로 둔다.&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;이 정도만 있어도 AI가 엉뚱한 위치에 파일을 만들 확률이 줄어든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로는 지침 파일이 코드 품질을 자동으로 보장한다기보다, &lt;b&gt;AI가 매번 새 프로젝트처럼 행동하지 않게 만드는 기준선&lt;/b&gt;에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;CLI형 AI 에이전트에서는 명령어 지침이 더 중요하다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex나 Claude Code 같은 최근 AI 코딩 도구는 단순히 코드 조각만 추천하는 자동완성 도구와 다르다. 프로젝트 파일을 읽고, 코드를 수정하고, 터미널에서 명령어를 실행하는 에이전트 성격이 강하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 지침 파일의 명령어 섹션이 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 단순히 텍스트를 생성하는 수준이라면 &amp;ldquo;실행 명령어&amp;rdquo;는 참고 정보에 가깝다. 하지만 AI가 직접 테스트, 린트, 빌드 명령어를 실행할 수 있다면 이야기가 달라진다. 이때 명령어 지침은 실제 실행 기준이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 프로젝트는 &lt;code&gt;pnpm&lt;/code&gt;을 쓰는데 AI가 습관적으로 &lt;code&gt;npm install&lt;/code&gt;을 실행하면 lock 파일이 꼬이거나 의존성 관리 방식이 달라질 수 있다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;## Commands

- Install dependencies: `pnpm install`
- Run dev server: `pnpm dev`
- Run lint: `pnpm lint`
- Run tests: `pnpm test`
- Do not use npm or yarn in this repository.&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;이런 식으로 적어두면 AI가 프로젝트에서 어떤 명령어를 우선 사용해야 하는지 판단하기 쉬워진다.&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;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;## Commands

- Run app: `npm run dev`
- Run app: `pnpm dev`
- Run tests: `npm test`
- Run tests: `pnpm test`&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;이렇게 쓰면 AI 입장에서는 어떤 명령어가 현재 기준인지 헷갈릴 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수정하면 이렇게 된다.&lt;/p&gt;
&lt;pre class=&quot;autohotkey&quot;&gt;&lt;code&gt;## Commands

- Use `pnpm` only.
- Run app: `pnpm dev`
- Run tests: `pnpm test`
- Do not use `npm install` or `yarn install`.&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;AI가 터미널에서 직접 실행할 수 있는 도구일수록, 명령어 지침은 짧고 명확해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Codex는 AGENTS.md를 읽는다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex에서는 &lt;code&gt;AGENTS.md&lt;/code&gt;가 프로젝트 지침 파일 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenAI 공식 문서 기준으로 Codex는 작업을 시작하기 전에 &lt;code&gt;AGENTS.md&lt;/code&gt; 파일을 읽는다. 전역 범위의 지침과 프로젝트별 지침을 계층적으로 적용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 위치는 프로젝트 루트다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;my-project/
├── AGENTS.md
├── package.json
├── src/
└── tests/&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;code&gt;AGENTS.md&lt;/code&gt;를 두면 Codex가 해당 프로젝트를 작업할 때 참고할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# AGENTS.md

## Project overview

This is a Next.js application using TypeScript and Tailwind CSS.

## Commands

- Install dependencies: `pnpm install`
- Run dev server: `pnpm dev`
- Run tests: `pnpm test`
- Run lint: `pnpm lint`

## Coding rules

- Use TypeScript for all new files.
- Prefer functional components.
- Keep server-side logic out of client components.
- Do not introduce new dependencies without explaining why.

## Before finishing

- Run `pnpm lint` when changing TypeScript files.
- Run related tests when modifying business logic.&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;ldquo;프로젝트를 설명하는 문서&amp;rdquo;와 &amp;ldquo;AI에게 시키는 행동 규칙&amp;rdquo;을 함께 넣는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 너무 길게 쓰면 오히려 안 좋다. AI가 읽어야 할 컨텍스트가 늘어나고, 중요한 규칙이 묻힐 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Claude Code는 CLAUDE.md가 맞다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 헷갈리기 쉬운 부분이 바로 Claude Code다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code는 기본적으로 &lt;code&gt;CLAUDE.md&lt;/code&gt;를 사용한다. &lt;code&gt;AGENTS.md&lt;/code&gt;가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 루트에 둘 수 있다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;my-project/
├── CLAUDE.md
├── package.json
├── src/
└── tests/&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;code&gt;.claude&lt;/code&gt; 디렉터리 안에 둘 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;my-project/
├── .claude/
│   └── CLAUDE.md
├── src/
└── package.json&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;Claude Code의 &lt;code&gt;CLAUDE.md&lt;/code&gt;에는 프로젝트 지침, 빌드 명령어, 테스트 명령어, 코드 스타일, 자주 하는 작업 흐름 등을 넣을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# CLAUDE.md

## Project context

This project is a FastAPI backend application.

## Development commands

- Create virtual environment: `python -m venv .venv`
- Install dependencies: `pip install -r requirements.txt`
- Run server: `fastapi dev main.py`
- Run tests: `pytest`

## Rules

- Use Pydantic v2 style.
- Keep route handlers thin.
- Put business logic in service modules.
- Do not commit secrets or `.env` files.
- Explain before changing authentication or database migration logic.&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;Claude Code에서 이미 &lt;code&gt;AGENTS.md&lt;/code&gt;를 쓰고 있는 저장소를 함께 쓰고 싶다면 &lt;code&gt;CLAUDE.md&lt;/code&gt; 안에서 가져오는 방식이 현실적이다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;@AGENTS.md

## Claude Code specific rules

- Use Plan Mode before changing authentication logic.
- Ask before running destructive commands.&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;code&gt;AGENTS.md&lt;/code&gt;에 두고, Claude Code에만 필요한 내용은 &lt;code&gt;CLAUDE.md&lt;/code&gt; 아래에 추가할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심볼릭 링크를 쓸 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;ln -s AGENTS.md CLAUDE.md&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;다만 Windows에서는 심볼릭 링크 생성에 권한 문제가 생길 수 있다. 그럴 때는 &lt;code&gt;CLAUDE.md&lt;/code&gt;에서 &lt;code&gt;@AGENTS.md&lt;/code&gt;로 가져오는 방식이 더 단순하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Cursor는 Rules와 AGENTS.md를 함께 봐야 한다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cursor는 예전부터 프로젝트 지침을 다루는 방식이 여러 번 바뀌었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전에는 &lt;code&gt;.cursorrules&lt;/code&gt; 파일을 많이 사용했다. 하지만 현재는 Cursor Rules를 중심으로 보는 것이 더 적합하다. 보통 프로젝트 안의 &lt;code&gt;.cursor/rules&lt;/code&gt; 디렉터리에 규칙을 나누어 관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;my-project/
├── .cursor/
│   └── rules/
│       ├── frontend.mdc
│       ├── testing.mdc
│       └── api.mdc
├── src/
└── package.json&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;Cursor Rules는 특정 파일, 디렉터리, 작업 맥락에 맞는 지침을 나눠서 관리하기 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 프론트엔드 규칙은 &lt;code&gt;frontend.mdc&lt;/code&gt;, 테스트 규칙은 &lt;code&gt;testing.mdc&lt;/code&gt;처럼 나눌 수 있다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;# frontend.mdc

- Use React functional components.
- Prefer existing UI components before creating new ones.
- Keep component files small and focused.
- Do not add new styling libraries.&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;Cursor 공식 문서에서는 Project, Team, User Rules와 함께 &lt;code&gt;AGENTS.md&lt;/code&gt;도 언급한다. 그래서 Cursor만 단독으로 쓴다면 Cursor Rules를 우선 정리하고, Codex 같은 다른 도구와 함께 쓴다면 &lt;code&gt;AGENTS.md&lt;/code&gt;를 공통 지침 파일로 두는 방식도 고려할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무적으로는 이렇게 나누면 덜 헷갈린다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;상황&lt;/th&gt;
&lt;th&gt;추천 방식&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Codex만 사용&lt;/td&gt;
&lt;td&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Code만 사용&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cursor만 사용&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.cursor/rules&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Codex + Claude Code 같이 사용&lt;/td&gt;
&lt;td&gt;&lt;code&gt;AGENTS.md&lt;/code&gt; + &lt;code&gt;CLAUDE.md&lt;/code&gt;에서 import&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Codex + Cursor 같이 사용&lt;/td&gt;
&lt;td&gt;&lt;code&gt;AGENTS.md&lt;/code&gt; + Cursor Rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;세 도구 모두 사용&lt;/td&gt;
&lt;td&gt;공통 규칙은 &lt;code&gt;AGENTS.md&lt;/code&gt;, 도구별 규칙은 각 도구 파일에 분리&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;README와 AGENTS.md는 어떻게 다를까&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;README.md&lt;/code&gt;와 &lt;code&gt;AGENTS.md&lt;/code&gt;는 비슷해 보이지만 목적이 다르다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;파일&lt;/th&gt;
&lt;th&gt;주 독자&lt;/th&gt;
&lt;th&gt;목적&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;README.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;사람&lt;/td&gt;
&lt;td&gt;프로젝트 소개, 설치, 실행 방법 설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;AI 코딩 에이전트&lt;/td&gt;
&lt;td&gt;작업 방식, 코드 수정 규칙, 명령어, 주의사항 전달&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Claude Code&lt;/td&gt;
&lt;td&gt;Claude Code 세션에서 반복 적용할 프로젝트 지침&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.cursor/rules&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cursor&lt;/td&gt;
&lt;td&gt;Cursor Agent가 작업할 때 참고할 규칙&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;README는 사람이 프로젝트를 이해하기 위한 문서다. 그래서 배경 설명, 설치 방법, 배포 방법, 기능 소개가 들어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 &lt;code&gt;AGENTS.md&lt;/code&gt;는 AI가 작업할 때 실수하지 않도록 돕는 문서다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 README에는 이렇게 쓸 수 있다.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;## Getting started

Run the development server:
&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;pnpm dev&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&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;code&gt;AGENTS.md&lt;/code&gt;에는 조금 더 행동 지침처럼 쓰는 편이 낫다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;## Commands

- Use `pnpm dev` for local development.
- Use `pnpm test` before finishing changes that touch business logic.
- Do not use npm or yarn in this repository.&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;AI에게는 &amp;ldquo;설명&amp;rdquo;보다 &amp;ldquo;어떤 상황에서 무엇을 해야 하는지&amp;rdquo;가 더 도움이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;지침 파일에 넣으면 좋은 내용&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 거창하게 쓸 필요는 없다. 실제로 반복해서 설명하게 되는 것부터 넣으면 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 프로젝트 개요&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 프로젝트의 큰 방향을 이해할 수 있게 짧게 적는다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;## Project overview

This is a FastAPI backend for a personal finance dashboard.
It provides REST APIs for transactions, categories, and monthly reports.&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;너무 긴 서비스 소개는 필요 없다. AI가 코드 수정 방향을 잡을 정도면 충분하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 기술 스택과 버전&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버전 차이로 코드가 달라지는 경우가 많다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;## Tech stack

- Python 3.12
- FastAPI
- Pydantic v2
- SQLAlchemy 2.x
- PostgreSQL
- pytest&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;특히 Pydantic v1과 v2, Next.js App Router와 Pages Router, React Server Components 같은 부분은 명시하는 편이 좋다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 실행 명령어&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 테스트나 린트를 실행할 때 헷갈리지 않도록 적는다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;## Commands

- Install dependencies: `pnpm install`
- Run dev server: `pnpm dev`
- Run lint: `pnpm lint`
- Run tests: `pnpm test`&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;Python 프로젝트라면 이렇게 쓸 수 있다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;## Commands

- Create venv: `python -m venv .venv`
- Install dependencies: `pip install -r requirements.txt`
- Run server: `fastapi dev main.py`
- Run tests: `pytest`&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;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. 디렉터리 구조 규칙&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 파일을 잘못된 위치에 만드는 문제를 줄일 수 있다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;## Project structure

- API routes: `src/routes/`
- Business logic: `src/services/`
- Database models: `src/models/`
- Request and response schemas: `src/schemas/`
- Tests: `tests/`&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드 프로젝트라면 이렇게 쓸 수 있다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;## Project structure

- Page routes: `src/app/`
- Shared UI components: `src/components/`
- Feature-specific components: `src/features/{feature}/components/`
- API clients: `src/lib/api/`
- Hooks: `src/hooks/`&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5. 코드 스타일&lt;/b&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;나쁜 예시는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;- Write clean code.
- Keep files organized.
- Make good components.&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;이런 문장은 너무 추상적이다. AI도 사람도 해석이 다를 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수정하면 이렇게 된다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;- Keep React components under 150 lines when possible.
- Move reusable logic into hooks.
- Use existing UI components before creating new ones.
- Do not add a new dependency without explaining why.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;6. 보안과 위험한 작업 규칙&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 도구는 파일을 수정하고 명령어를 실행할 수 있다. 그래서 위험한 작업은 명확히 제한하는 것이 좋다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;## Safety rules

- Do not modify `.env` files.
- Do not print secrets, API keys, or tokens.
- Ask before changing authentication logic.
- Ask before running database migration commands.
- Do not run destructive commands such as `rm -rf`, `DROP TABLE`, or forced git reset.&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;ldquo;작업 전 주의사항&amp;rdquo;에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;지침 파일에 넣으면 안 되는 내용&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지침 파일은 길수록 좋은 문서가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오히려 너무 길면 AI가 중요한 규칙을 놓칠 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 오래된 규칙&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 프로젝트가 npm에서 pnpm으로 바뀌었는데 예전 명령어가 남아 있으면 AI가 잘못된 명령어를 실행할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;- Run `npm install`
- Run `pnpm install`&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 충돌하는 규칙은 피해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나만 남기는 편이 낫다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;- Use `pnpm install`. Do not use npm or yarn.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 너무 일반적인 원칙&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;- Write good code.
- Follow best practices.
- Make it scalable.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 문장은 거의 도움이 되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 프로젝트에서 실제로 반복되는 실수로 바꿔야 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;- Do not put business logic inside route handlers.
- Add tests for service functions when changing calculation logic.
- Reuse existing error response format from `src/errors/`.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 비밀 정보&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지침 파일은 보통 저장소에 커밋된다. 그래서 API 키, 토큰, 내부 비밀번호, 개인 계정 정보는 절대 넣으면 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나쁜 예시는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;- Use production API key: sk-...
- Admin password is ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 이렇게 적어야 한다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;- Read required environment variables from `.env`.
- Do not create or expose real API keys in examples.
- Use placeholder values in documentation and tests.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. 너무 긴 아키텍처 문서&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;긴 아키텍처 설명은 별도 문서로 분리하는 편이 낫다.&lt;/p&gt;
&lt;pre class=&quot;dos&quot;&gt;&lt;code&gt;docs/architecture.md
docs/database.md
docs/api-style.md&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 지침 파일에서는 필요한 문서만 짧게 참조한다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;## References

- Architecture overview: `docs/architecture.md`
- API response format: `docs/api-style.md`&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;AI가 필요할 때 찾아 읽을 수 있도록 길을 알려주는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;바로 쓸 수 있는 AGENTS.md 예시&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예시는 Codex 기준으로 바로 시작할 수 있는 단순한 &lt;code&gt;AGENTS.md&lt;/code&gt; 템플릿이다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;# AGENTS.md

## Project overview

This project is a web application maintained by a small development team.
Follow the existing code style and avoid unnecessary rewrites.

## Tech stack

- TypeScript
- React
- Node.js
- pnpm

## Commands

- Install dependencies: `pnpm install`
- Run development server: `pnpm dev`
- Run lint: `pnpm lint`
- Run tests: `pnpm test`
- Do not use npm or yarn in this repository.

## Project structure

- Application code: `src/`
- Shared components: `src/components/`
- Feature modules: `src/features/`
- Utility functions: `src/lib/`
- Tests: `tests/`

## Coding rules

- Use TypeScript for new files.
- Reuse existing components before creating new ones.
- Do not add new dependencies unless necessary.
- Keep changes focused on the requested task.
- Do not perform broad refactoring unless explicitly requested.

## Safety rules

- Do not modify `.env` files.
- Do not expose secrets, tokens, or API keys.
- Ask before changing authentication, payment, or database migration logic.
- Do not run destructive commands without confirmation.

## Before finishing

- Run lint when changing TypeScript files.
- Run relevant tests when changing business logic.
- Summarize what changed and mention any tests that were not run.&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;중요한 건 처음부터 완성형 문서를 만들려고 하지 않는 것이다. AI가 같은 실수를 반복할 때마다 한 줄씩 추가하는 편이 더 현실적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Claude Code용 CLAUDE.md 예시&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code만 쓴다면 &lt;code&gt;CLAUDE.md&lt;/code&gt;로 작성한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# CLAUDE.md

## Project overview

This is a backend API project.
Keep changes small and explain risky decisions before editing.

## Commands

- Run server: `fastapi dev main.py`
- Run tests: `pytest`
- Run formatter: `ruff format .`
- Run linter: `ruff check .`

## Architecture rules

- Route handlers should stay thin.
- Put business logic in service modules.
- Put request and response models in schema modules.
- Do not mix database models and API schemas.

## Safety rules

- Do not edit `.env` files.
- Do not print secrets or tokens.
- Ask before changing authentication logic.
- Ask before changing migration files.

## Workflow

- First inspect related files.
- Make the smallest change that solves the issue.
- Run relevant tests when possible.
- Explain any skipped tests.&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;code&gt;AGENTS.md&lt;/code&gt;가 있는 프로젝트에서 Claude Code도 같이 쓰려면 이렇게 구성할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;@AGENTS.md

## Claude Code specific rules

- Use Plan Mode before large refactors.
- Ask before running database commands.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 중복을 줄이는 데 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공통 규칙은 &lt;code&gt;AGENTS.md&lt;/code&gt;에 두고, Claude Code 전용 규칙만 &lt;code&gt;CLAUDE.md&lt;/code&gt;에 남기면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Cursor Rules 예시&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cursor에서는 &lt;code&gt;.cursor/rules&lt;/code&gt;에 주제별 규칙을 나눠 둘 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 테스트 규칙은 이렇게 분리할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;.cursor/rules/testing.mdc&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;# Testing rules

- Add or update tests when changing business logic.
- Prefer existing test helpers.
- Do not introduce a new testing library.
- If tests are not run, explain why.&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;API 규칙은 따로 둘 수 있다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;.cursor/rules/api.mdc&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# API rules

- Follow the existing error response format.
- Validate input at the boundary.
- Do not return raw database models from API handlers.
- Keep authentication checks explicit.&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;Cursor만 쓴다면 이 방식이 깔끔하다. 여러 도구를 같이 쓴다면 공통 규칙은 &lt;code&gt;AGENTS.md&lt;/code&gt;에 두고, Cursor에서만 필요한 규칙은 &lt;code&gt;.cursor/rules&lt;/code&gt;에 분리하는 방식이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;도구별 추천 구성&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 도구를 하나만 쓸 때는 단순하게 가면 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Codex만 쓰는 경우&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;my-project/
├── AGENTS.md
├── src/
└── package.json&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Claude Code만 쓰는 경우&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;my-project/
├── CLAUDE.md
├── src/
└── package.json&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 다음처럼 둘 수 있다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;my-project/
├── .claude/
│   └── CLAUDE.md
├── src/
└── package.json&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Cursor만 쓰는 경우&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;my-project/
├── .cursor/
│   └── rules/
│       ├── coding.mdc
│       ├── testing.mdc
│       └── api.mdc
├── src/
└── package.json&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Codex와 Claude Code를 같이 쓰는 경우&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;my-project/
├── AGENTS.md
├── CLAUDE.md
├── src/
└── package.json&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;code&gt;CLAUDE.md&lt;/code&gt;는 이렇게 작성한다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;@AGENTS.md

## Claude Code specific rules

- Use Plan Mode before large refactors.
- Ask before running destructive commands.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;세 도구를 모두 쓰는 경우&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;my-project/
├── AGENTS.md
├── CLAUDE.md
├── .cursor/
│   └── rules/
│       ├── coding.mdc
│       └── testing.mdc
├── src/
└── package.json&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;code&gt;AGENTS.md&lt;/code&gt;를 공통 기준으로 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt;는 Claude Code가 &lt;code&gt;AGENTS.md&lt;/code&gt;를 가져오게 만들고, &lt;code&gt;.cursor/rules&lt;/code&gt;에는 Cursor에서만 세밀하게 적용할 규칙을 둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 공통 규칙은 &lt;code&gt;AGENTS.md&lt;/code&gt;에 둔다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;# AGENTS.md

## Common rules

- Use pnpm only.
- Do not edit `.env` files.
- Keep changes focused.
- Run relevant tests after changing business logic.&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;Claude Code 전용 규칙은 &lt;code&gt;CLAUDE.md&lt;/code&gt;에 둔다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;@AGENTS.md

## Claude Code specific rules

- Ask before running database commands.
- Explain skipped tests before finishing.&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;Cursor 전용 규칙은 &lt;code&gt;.cursor/rules&lt;/code&gt;에 둔다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;# .cursor/rules/testing.mdc

- Prefer existing test helpers.
- Add tests near the changed feature.
- Do not introduce a new testing library.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 나누면 세 도구를 같이 쓰더라도 같은 규칙을 여러 파일에 반복해서 쓰지 않아도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;자주 하는 오해&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. AGENTS.md를 만들면 AI가 무조건 그대로 따를까?&lt;/b&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;지침 파일은 강제 설정이 아니라 컨텍스트에 가깝다. AI가 참고할 가능성을 높여주는 문서이지, 보안 정책처럼 무조건 차단하는 장치는 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정말 막아야 하는 작업이 있다면 도구의 권한 설정, 승인 모드, 훅, CI, 브랜치 보호 규칙 같은 실제 제어 장치가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &amp;ldquo;DB migration은 확인 없이 실행하지 마&amp;rdquo;라고 적는 것은 도움이 된다. 하지만 실제 운영 DB 접근 권한을 막는 것은 지침 파일이 아니라 권한 관리에서 해야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. README 내용을 그대로 복사하면 될까?&lt;/b&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;README는 사람을 위한 문서다. AI 지침 파일은 작업을 위한 문서다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;README의 긴 소개보다 다음 같은 짧은 규칙이 더 효과적이다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;- Use existing service patterns from `src/services/`.
- Do not create a new architecture for small changes.
- Run `pnpm test` after changing business logic.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 규칙을 많이 넣을수록 좋을까?&lt;/b&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;규칙이 많아질수록 중요한 지침이 묻힌다. 서로 충돌하는 규칙이 생길 가능성도 커진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 30~80줄 정도로 시작하는 편이 좋다. 이후 AI가 반복해서 실수하는 부분만 추가한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. 개인 취향도 커밋해야 할까?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀 공통 규칙과 개인 취향은 분리하는 편이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 팀 전체가 따라야 하는 규칙은 커밋해도 된다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;- Use pnpm.
- Run tests before finishing backend changes.
- Follow the existing API response format.&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;Claude Code라면 &lt;code&gt;CLAUDE.local.md&lt;/code&gt; 같은 로컬 파일을 쓸 수 있고, Cursor나 Codex도 개인 설정과 팀 공유 파일을 구분하는 방식으로 운영하는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;지침 파일은 한 번에 완성하지 않는 편이 낫다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 완벽한 &lt;code&gt;AGENTS.md&lt;/code&gt;를 만들려고 하면 대부분 너무 길어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 좋은 방식은 실제로 AI가 실수한 지점을 기준으로 업데이트하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 AI가 npm을 사용했다면 이렇게 추가한다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;- Use pnpm in this repository. Do not use npm or yarn.&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;AI가 라우터 안에 비즈니스 로직을 길게 넣었다면 이렇게 추가한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;- Keep route handlers thin. Move business logic into service modules.&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;AI가 테스트 없이 작업을 끝냈다면 이렇게 추가한다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;- Run relevant tests when changing business logic. If tests cannot be run, explain why.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 작성하면 지침 파일이 실제 프로젝트의 문제를 반영하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 인터넷에서 복사한 긴 템플릿을 그대로 넣으면 프로젝트와 맞지 않는 규칙이 섞일 수 있다. 이 부분을 오해하기 쉽다. 지침 파일은 많을수록 좋은 게 아니라, &lt;b&gt;현재 프로젝트에서 반복되는 실수를 줄일수록 좋은 문서&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;정리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;는 AI 코딩 에이전트에게 프로젝트의 작업 규칙을 알려주는 파일이다. Codex에서는 핵심 지침 파일로 쓰이며, 프로젝트 구조, 실행 명령어, 코드 스타일, 주의사항을 적어두는 데 적합하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 Claude Code는 &lt;code&gt;AGENTS.md&lt;/code&gt;가 아니라 &lt;code&gt;CLAUDE.md&lt;/code&gt;가 기본이다. 이미 &lt;code&gt;AGENTS.md&lt;/code&gt;를 쓰는 프로젝트라면 &lt;code&gt;CLAUDE.md&lt;/code&gt;에서 &lt;code&gt;@AGENTS.md&lt;/code&gt;로 가져오는 방식이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cursor는 Cursor Rules를 중심으로 보는 것이 좋다. &lt;code&gt;.cursor/rules&lt;/code&gt;에 규칙을 나눠두면 작업 범위별로 관리하기 쉽다. 여러 도구를 함께 쓴다면 공통 규칙은 &lt;code&gt;AGENTS.md&lt;/code&gt;, Claude 전용 규칙은 &lt;code&gt;CLAUDE.md&lt;/code&gt;, Cursor 전용 규칙은 &lt;code&gt;.cursor/rules&lt;/code&gt;에 나누는 구성이 현실적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지침 파일에 모든 것을 넣을 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 넣을 내용은 반복해서 설명하게 되는 것들이다.&lt;/p&gt;
&lt;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;li&gt;어떤 파일은 건드리면 안 되는지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 Codex나 Claude Code처럼 터미널에서 명령어를 직접 실행할 수 있는 도구를 쓴다면, 실행 명령어와 금지 명령어를 더 명확하게 적어두는 편이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 좋은 지침 파일은 긴 문서가 아니라, AI가 같은 실수를 반복하지 않게 만드는 짧고 정확한 작업 기준이다.&lt;/p&gt;</description>
      <category>AI/AI 코딩</category>
      <category>AGENTSmd</category>
      <category>AI코딩</category>
      <category>claudecode</category>
      <category>codex</category>
      <category>CursorRules</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/62</guid>
      <comments>https://notebase.tistory.com/entry/agents-md-codex-claude-code-cursor-rules#entry62comment</comments>
      <pubDate>Fri, 5 Jun 2026 11:23:55 +0900</pubDate>
    </item>
    <item>
      <title>FastAPI 환경변수 설정: .env로 DB 주소와 비밀키 분리하기</title>
      <link>https://notebase.tistory.com/entry/fastapi-env-database-secret-key</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI에서 &lt;code&gt;.env&lt;/code&gt;와 pydantic-settings를 사용해 DB 주소, SECRET_KEY, 디버그 설정을 코드와 분리하는 방법을 예제로 정리합니다.&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;FastAPI 환경변수 설정을 제대로 해두면 DB 주소, JWT 비밀키, 디버그 옵션을 코드에서 분리할 수 있다. 처음에는 &lt;code&gt;main.py&lt;/code&gt;에 직접 적는 게 편해 보이지만, GitHub에 올리거나 서버에 배포하는 순간 문제가 될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 &lt;b&gt;2026년 6월 기준 FastAPI 최신 버전과 Pydantic v2 흐름&lt;/b&gt;을 기준으로 작성했다. 설정 관리는 &lt;code&gt;pydantic-settings&lt;/code&gt;를 사용하고, 로컬 개발 환경에서는 &lt;code&gt;.env&lt;/code&gt; 파일을 함께 사용하는 방식으로 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경변수 설정은 단순히 &lt;code&gt;.env&lt;/code&gt; 파일을 하나 만드는 작업이 아니다.&lt;br /&gt;&lt;b&gt;개발 환경과 운영 환경의 설정을 분리하고, 실수로 비밀값이 코드 저장소에 올라가지 않게 막는 구조&lt;/b&gt;에 가깝다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;왜 FastAPI에서 환경변수를 분리해야 할까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래처럼 DB 주소와 비밀키를 코드에 직접 넣었다고 해보자.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;# main.py

DATABASE_URL = &quot;sqlite:///./app.db&quot;
SECRET_KEY = &quot;my-secret-key&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;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;개발 DB와 운영 DB 주소를 바꾸기 어렵다.&lt;/li&gt;
&lt;li&gt;JWT 비밀키가 코드에 그대로 노출된다.&lt;/li&gt;
&lt;li&gt;GitHub에 올렸을 때 민감한 값이 함께 올라갈 수 있다.&lt;/li&gt;
&lt;li&gt;Docker, 배포 서버, CI/CD 환경에서 설정을 바꾸기 불편하다.&lt;/li&gt;
&lt;li&gt;팀원이 프로젝트를 실행할 때 필요한 설정값을 파악하기 어렵다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점은 &lt;code&gt;.env&lt;/code&gt;가 보안을 완성해 주는 도구는 아니라는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;.env&lt;/code&gt;는 주로 &lt;b&gt;로컬 개발 환경에서 설정값을 분리하기 위한 파일&lt;/b&gt;이다.&lt;br /&gt;운영 환경에서는 서버 환경변수, Docker secret, AWS Secrets Manager, GCP Secret Manager 같은 별도 비밀값 관리 방식을 쓰는 경우가 많다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;이 글에서 만들 구조&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시는 FastAPI 프로젝트에서 자주 쓰는 형태로 구성한다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;fastapi-env-example/
├── app/
│   ├── main.py
│   ├── config.py
│   └── database.py
├── .env
├── .env.example
├── .gitignore
└── requirements.txt&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;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;파일&lt;/th&gt;
&lt;th&gt;역할&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.env&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;실제 로컬 환경변수 값 저장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.env.example&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;필요한 환경변수 이름만 공유&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.gitignore&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.env&lt;/code&gt;가 Git에 올라가지 않도록 차단&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;app/config.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;환경변수를 읽는 설정 클래스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;app/database.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;DB 연결 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;app/main.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;FastAPI 앱에서 설정값 사용&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;code&gt;auth.py&lt;/code&gt;, &lt;code&gt;routers/&lt;/code&gt;, &lt;code&gt;schemas/&lt;/code&gt;, &lt;code&gt;models/&lt;/code&gt; 같은 파일이 더 생길 수 있다.&lt;br /&gt;하지만 환경변수 흐름을 이해하는 데는 위 구조가 가장 단순하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. 필요한 패키지 설치하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 FastAPI와 설정 관리에 필요한 패키지를 설치한다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;pip install fastapi uvicorn pydantic-settings python-dotenv&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;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;패키지&lt;/th&gt;
&lt;th&gt;역할&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fastapi&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;FastAPI 웹 프레임워크&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;uvicorn&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;FastAPI 앱 실행 서버&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pydantic-settings&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;환경변수 기반 설정 클래스 관리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;python-dotenv&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.env&lt;/code&gt; 파일 로드 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;pydantic-settings&lt;/code&gt;는 Pydantic v2 흐름에서 설정 관리를 담당한다.&lt;br /&gt;과거 Pydantic v1 예제를 보면 &lt;code&gt;pydantic.BaseSettings&lt;/code&gt;를 사용하는 경우가 있는데, 최신 흐름에서는 &lt;code&gt;pydantic_settings.BaseSettings&lt;/code&gt;를 사용하는 방식으로 이해하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;requirements.txt&lt;/code&gt;로 관리한다면 아래처럼 적을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;fastapi
uvicorn
pydantic-settings
python-dotenv&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;JWT 예시 코드까지 직접 실행할 계획이라면 &lt;code&gt;pyjwt&lt;/code&gt;도 추가한다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;fastapi
uvicorn
pydantic-settings
python-dotenv
pyjwt&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;파이썬에서 JWT를 사용할 때는 보통 &lt;code&gt;PyJWT&lt;/code&gt; 패키지를 설치한다.&lt;br /&gt;설치 명령어는 아래처럼 작성한다.&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;pip install pyjwt&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;code&gt;pip install jwt&lt;/code&gt;를 실행하는 경우가 있다.&lt;br /&gt;그러면 예제에서 &lt;code&gt;import jwt&lt;/code&gt;는 되는 것처럼 보여도, 실제로 &lt;code&gt;jwt.encode()&lt;/code&gt;를 호출할 때 문제가 생길 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. .env 파일 만들기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 루트에 &lt;code&gt;.env&lt;/code&gt; 파일을 만든다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;DATABASE_URL=sqlite:///./app.db
SECRET_KEY=local-dev-secret-key
ACCESS_TOKEN_EXPIRE_MINUTES=30
DEBUG=true&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;여기서는 설명을 위해 SQLite 주소를 넣었다.&lt;br /&gt;PostgreSQL을 사용한다면 이런 형태가 될 수 있다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;DATABASE_URL=postgresql://user:password@localhost:5432/mydb
SECRET_KEY=local-dev-secret-key
ACCESS_TOKEN_EXPIRE_MINUTES=30
DEBUG=true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의할 점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;.env&lt;/code&gt; 파일 안의 값은 기본적으로 문자열 형태로 읽힌다.&lt;br /&gt;그래서 &lt;code&gt;30&lt;/code&gt;, &lt;code&gt;true&lt;/code&gt;처럼 적어도 코드에서 숫자나 불리언으로 안전하게 쓰려면 변환 과정이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pydantic Settings를 쓰면 이 변환과 검증을 설정 클래스에서 처리할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. .env.example 파일 만들기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;.env&lt;/code&gt;는 Git에 올리면 안 된다.&lt;br /&gt;대신 어떤 환경변수가 필요한지 알려주는 &lt;code&gt;.env.example&lt;/code&gt; 파일을 만든다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;DATABASE_URL=
SECRET_KEY=
ACCESS_TOKEN_EXPIRE_MINUTES=30
DEBUG=false&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일에는 실제 비밀번호나 비밀키를 넣지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀 프로젝트라면 새로 합류한 사람이 &lt;code&gt;.env.example&lt;/code&gt;을 보고 &lt;code&gt;.env&lt;/code&gt;를 직접 만들 수 있다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;cp .env.example .env&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;Windows PowerShell에서는 아래처럼 복사할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;Copy-Item .env.example .env&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. .gitignore에 .env 추가하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;.env&lt;/code&gt; 파일이 Git에 올라가지 않도록 &lt;code&gt;.gitignore&lt;/code&gt;에 추가한다.&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;.env
.venv/
__pycache__/
*.pyc&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;code&gt;.env&lt;/code&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 &lt;code&gt;.env&lt;/code&gt;를 Git에 커밋한 적이 있다면 &lt;code&gt;.gitignore&lt;/code&gt;에 추가하는 것만으로는 부족하다.&lt;br /&gt;Git 추적 대상에서 제거해야 한다.&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;git rm --cached .env&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음 커밋한다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;git commit -m &quot;Remove .env from tracking&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;단, 이미 GitHub 같은 원격 저장소에 비밀키가 올라갔다면 단순 삭제로 끝내면 안 된다.&lt;br /&gt;노출된 키는 폐기하고 새 키로 교체해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 JWT &lt;code&gt;SECRET_KEY&lt;/code&gt;가 올라갔다면 새 키를 만들어 교체해야 한다.&lt;br /&gt;외부 API Key나 DB 비밀번호가 올라갔다면 해당 서비스에서 키를 재발급하거나 비밀번호를 바꾸는 게 안전하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;5. config.py에서 환경변수 읽기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 &lt;code&gt;app/config.py&lt;/code&gt; 파일을 만든다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;# app/config.py

from functools import lru_cache

from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
    database_url: str
    secret_key: str
    access_token_expire_minutes: int = 30
    debug: bool = False

    model_config = SettingsConfigDict(
        env_file=&quot;.env&quot;,
        env_file_encoding=&quot;utf-8&quot;,
    )


@lru_cache
def get_settings() -&amp;gt; Settings:
    return Settings()&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;code&gt;Settings&lt;/code&gt; 클래스다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class Settings(BaseSettings):
    database_url: str
    secret_key: str&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;이렇게 적으면 Pydantic Settings가 환경변수에서 값을 읽는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;.env&lt;/code&gt; 파일에는 대문자로 적었다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;DATABASE_URL=sqlite:///./app.db
SECRET_KEY=local-dev-secret-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;코드에서는 소문자 필드로 썼다.&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;database_url: str
secret_key: str&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;code&gt;.env&lt;/code&gt;에는 대문자, Python 코드에는 소문자 필드를 쓰면 읽기 좋다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;6. @lru_cache는 왜 사용할까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;get_settings()&lt;/code&gt; 함수 위에 &lt;code&gt;@lru_cache&lt;/code&gt;를 붙였다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;@lru_cache
def get_settings() -&amp;gt; Settings:
    return Settings()&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;FastAPI 앱은 요청이 들어올 때마다 여러 의존성을 실행할 수 있다.&lt;br /&gt;설정 파일을 매 요청마다 다시 읽을 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;@lru_cache&lt;/code&gt;를 붙이면 처음 한 번 생성한 설정 객체를 캐시해서 사용한다.&lt;br /&gt;개발자가 보기에도 &lt;code&gt;get_settings()&lt;/code&gt;를 통해 설정을 가져오는 흐름이 명확해진다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;7. main.py에서 설정값 사용하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 FastAPI 앱에서 설정값을 사용해보자.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;# app/main.py

from fastapi import Depends, FastAPI

from app.config import Settings, get_settings

app = FastAPI()


@app.get(&quot;/&quot;)
def read_root(settings: Settings = Depends(get_settings)):
    return {
        &quot;message&quot;: &quot;FastAPI environment settings example&quot;,
        &quot;database_url&quot;: settings.database_url,
        &quot;debug&quot;: settings.debug,
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버를 실행한다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;uvicorn app.main:app --reload&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저에서 아래 주소로 접속한다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://127.0.0.1:8000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응답 예시는 다음과 비슷하다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;message&quot;: &quot;FastAPI environment settings example&quot;,
  &quot;database_url&quot;: &quot;sqlite:///./app.db&quot;,
  &quot;debug&quot;: true
}&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;code&gt;database_url&lt;/code&gt;을 응답에 포함했다.&lt;br /&gt;하지만 실제 서비스에서는 DB 주소도 내부 정보에 가까우므로 외부 API 응답에 노출하지 않는 편이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;secret_key&lt;/code&gt;는 절대 응답으로 반환하면 안 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;8. SECRET_KEY는 어디에 사용할까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;SECRET_KEY&lt;/code&gt;는 보통 JWT 토큰 서명에 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JWT 예시를 실행하려면 PyJWT를 설치해야 한다.&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;pip install pyjwt&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;code&gt;pip install jwt&lt;/code&gt;가 아니라 &lt;code&gt;pip install pyjwt&lt;/code&gt;다.&lt;br /&gt;패키지 이름은 &lt;code&gt;pyjwt&lt;/code&gt;지만, 코드에서는 아래처럼 &lt;code&gt;import jwt&lt;/code&gt;로 사용한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# 예시 코드

from datetime import datetime, timedelta, timezone

import jwt

from app.config import get_settings


def create_access_token(data: dict) -&amp;gt; str:
    settings = get_settings()

    expire = datetime.now(timezone.utc) + timedelta(
        minutes=settings.access_token_expire_minutes
    )

    payload = data.copy()
    payload.update({&quot;exp&quot;: expire})

    token = jwt.encode(
        payload,
        settings.secret_key,
        algorithm=&quot;HS256&quot;,
    )

    return token&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 JWT 흐름을 보여주기 위한 예시다.&lt;br /&gt;실제 운영에서는 토큰 만료 시간, 알고리즘, refresh token, 비밀번호 해싱, HTTPS 적용까지 함께 고려해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점은 &lt;code&gt;settings.secret_key&lt;/code&gt;를 코드에 직접 적지 않는다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나쁜 예시는 아래와 같다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;token = jwt.encode(payload, &quot;my-secret-key&quot;, algorithm=&quot;HS256&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;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;settings = get_settings()

token = jwt.encode(
    payload,
    settings.secret_key,
    algorithm=&quot;HS256&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;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;9. DB 연결 코드에서 DATABASE_URL 사용하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQLAlchemy나 SQLModel을 사용할 때도 DB 주소를 환경변수에서 가져올 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시는 SQLAlchemy 기준이다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# app/database.py

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

from app.config import get_settings

settings = get_settings()

engine = create_engine(
    settings.database_url,
    connect_args={&quot;check_same_thread&quot;: False},
)

SessionLocal = sessionmaker(
    autocommit=False,
    autoflush=False,
    bind=engine,
)&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;SQLite를 사용할 때는 &lt;code&gt;connect_args={&quot;check_same_thread&quot;: False}&lt;/code&gt;를 자주 사용한다.&lt;br /&gt;하지만 PostgreSQL 같은 DB에서는 이 옵션이 필요하지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PostgreSQL로 바꾸면 예시는 이렇게 달라질 수 있다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;engine = create_engine(settings.database_url)&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;즉, 환경변수로 DB 주소를 분리하면 코드 전체를 크게 바꾸지 않고 DB 연결 대상을 바꿀 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;10. 흔한 오류 1: .env 파일을 찾지 못하는 경우&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 자주 만나는 문제는 &lt;code&gt;.env&lt;/code&gt; 파일 위치다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 프로젝트 구조가 아래와 같다고 해보자.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;fastapi-env-example/
├── app/
│   ├── main.py
│   └── config.py
└── .env&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;code&gt;env_file=&quot;.env&quot;&lt;/code&gt;는 보통 서버를 실행하는 현재 작업 디렉터리를 기준으로 찾는다.&lt;br /&gt;프로젝트 루트에서 실행하면 정상이다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;uvicorn app.main:app --reload&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;code&gt;.env&lt;/code&gt;를 못 찾을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴 때는 먼저 현재 위치를 확인한다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot;&gt;&lt;code&gt;pwd&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;Windows PowerShell이라면 다음 명령어를 쓸 수 있다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;Get-Location&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 단순한 해결 방법은 프로젝트 루트에서 서버를 실행하는 것이다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;cd fastapi-env-example
uvicorn app.main:app --reload&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Tip. 실행 위치와 상관없이 루트의 .env를 읽고 싶다면&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트가 커지면 실행 위치가 바뀌어도 항상 프로젝트 루트의 &lt;code&gt;.env&lt;/code&gt;를 읽고 싶을 수 있다.&lt;br /&gt;이럴 때는 &lt;code&gt;config.py&lt;/code&gt;에서 절대 경로를 계산해 넣을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;# app/config.py

from functools import lru_cache
from pathlib import Path

from pydantic_settings import BaseSettings, SettingsConfigDict

BASE_DIR = Path(__file__).resolve().parent.parent


class Settings(BaseSettings):
    database_url: str
    secret_key: str
    access_token_expire_minutes: int = 30
    debug: bool = False

    model_config = SettingsConfigDict(
        env_file=BASE_DIR / &quot;.env&quot;,
        env_file_encoding=&quot;utf-8&quot;,
    )


@lru_cache
def get_settings() -&amp;gt; Settings:
    return Settings()&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;code&gt;Path(__file__).resolve()&lt;/code&gt;는 현재 파일의 실제 경로를 찾는다.&lt;br /&gt;&lt;code&gt;parent.parent&lt;/code&gt;는 &lt;code&gt;app/config.py&lt;/code&gt;에서 두 단계 위, 즉 프로젝트 루트를 가리킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 편리하지만, 프로젝트 구조가 바뀌면 &lt;code&gt;BASE_DIR&lt;/code&gt; 계산도 함께 확인해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;11. 흔한 오류 2: 필수 환경변수가 없을 때&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;.env&lt;/code&gt;에서 &lt;code&gt;SECRET_KEY&lt;/code&gt;를 빼고 실행하면 Pydantic 검증 오류가 날 수 있다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;DATABASE_URL=sqlite:///./app.db
ACCESS_TOKEN_EXPIRE_MINUTES=30
DEBUG=true&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;code&gt;Settings&lt;/code&gt;를 만들면 &lt;code&gt;secret_key&lt;/code&gt; 값이 없기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류 메시지는 버전에 따라 조금 다를 수 있지만, 대략 이런 형태다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Field required [type=missing, input_value=..., input_type=dict]&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;br /&gt;앱이 잘못된 설정으로 조용히 실행되는 것보다, 시작 단계에서 필요한 값이 없다고 알려주는 편이 안전하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결 방법은 &lt;code&gt;.env&lt;/code&gt;에 값을 추가하는 것이다.&lt;/p&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;SECRET_KEY=local-dev-secret-key&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;12. 흔한 오류 3: bool 값이 예상과 다르게 동작하는 경우&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;.env&lt;/code&gt;에는 모든 값이 문자열로 들어간다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;DEBUG=false&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;code&gt;os.getenv()&lt;/code&gt;만 사용하면 &lt;code&gt;&quot;false&quot;&lt;/code&gt;라는 문자열이 반환된다.&lt;br /&gt;문자열 &lt;code&gt;&quot;false&quot;&lt;/code&gt;는 Python 조건문에서 True처럼 동작할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;lua&quot;&gt;&lt;code&gt;import os

debug = os.getenv(&quot;DEBUG&quot;)

if debug:
    print(&quot;debug mode&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;code&gt;DEBUG=false&lt;/code&gt;여도 &lt;code&gt;&quot;false&quot;&lt;/code&gt; 문자열이 비어 있지 않기 때문에 조건문이 실행될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pydantic Settings를 쓰면 &lt;code&gt;debug: bool&lt;/code&gt; 타입으로 선언했을 때 적절히 bool 값으로 변환된다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class Settings(BaseSettings):
    debug: bool = False&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;br /&gt;단순히 값을 가져오는 것이 아니라, 타입 변환과 검증까지 함께 처리할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;13. 흔한 오류 4: jwt 패키지를 잘못 설치한 경우&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JWT 예제를 따라 하다가 아래 같은 오류를 만날 수 있다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;AttributeError: module 'jwt' has no attribute 'encode'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 먼저 설치한 패키지를 확인해보는 것이 좋다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;pip list | grep -i jwt&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;Windows PowerShell에서는 아래처럼 확인할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;pip list | findstr /i jwt&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;code&gt;jwt&lt;/code&gt;라는 다른 패키지를 설치했다면 제거하고 &lt;code&gt;PyJWT&lt;/code&gt;를 다시 설치한다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;pip uninstall jwt
pip install pyjwt&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;code&gt;import jwt&lt;/code&gt;를 사용한다.&lt;/p&gt;
&lt;pre class=&quot;elm&quot;&gt;&lt;code&gt;import jwt&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;헷갈리기 쉬운 부분은 패키지 설치 이름과 import 이름이 다르다는 점이다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;값&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;설치 명령어&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pip install pyjwt&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;코드 import&lt;/td&gt;
&lt;td&gt;&lt;code&gt;import jwt&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;주요 사용&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jwt.encode()&lt;/code&gt;, &lt;code&gt;jwt.decode()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분은 FastAPI 문제가 아니라 Python 패키지 이름 때문에 생기는 혼동에 가깝다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;14. 운영 환경에서는 .env를 그대로 써도 될까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬 개발에서는 &lt;code&gt;.env&lt;/code&gt;를 써도 괜찮다.&lt;br /&gt;하지만 운영 환경에서는 상황에 따라 다르게 접근해야 한다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;환경&lt;/th&gt;
&lt;th&gt;권장 방식&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;로컬 개발&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.env&lt;/code&gt; 파일&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;개인 테스트 서버&lt;/td&gt;
&lt;td&gt;서버 환경변수 또는 제한된 &lt;code&gt;.env&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--env-file&lt;/code&gt;, Docker Compose 환경변수, secret 관리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;클라우드 배포&lt;/td&gt;
&lt;td&gt;플랫폼 환경변수 또는 Secret Manager&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;팀/회사 운영&lt;/td&gt;
&lt;td&gt;AWS Secrets Manager, GCP Secret Manager, Vault, CI/CD Secret&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;Secret Manager는 비밀번호, API Key, 토큰 같은 민감한 값을 안전하게 저장하고 접근 권한을 관리하는 서비스다.&lt;br /&gt;AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault 같은 도구가 여기에 해당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영 서버에 &lt;code&gt;.env&lt;/code&gt;를 둘 수는 있다.&lt;br /&gt;다만 권한 관리, 백업, 배포 로그 노출, 서버 접근 권한까지 함께 봐야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 아래 값은 더 조심해야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DB 비밀번호&lt;/li&gt;
&lt;li&gt;JWT &lt;code&gt;SECRET_KEY&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;OAuth Client Secret&lt;/li&gt;
&lt;li&gt;외부 API Key&lt;/li&gt;
&lt;li&gt;SMTP 비밀번호&lt;/li&gt;
&lt;li&gt;관리자 초기 비밀번호&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;.env&lt;/code&gt;를 쓴다고 해서 비밀값이 자동으로 안전해지는 것은 아니다.&lt;br /&gt;Git에 올리지 않고, 로그에 찍지 않고, 노출되면 즉시 교체할 수 있어야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;15. 환경변수 이름은 어떻게 정하면 좋을까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은 프로젝트라면 아래처럼 단순하게 써도 된다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;DATABASE_URL=
SECRET_KEY=
DEBUG=&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트가 커지면 접두사를 붙이는 것도 좋다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;APP_DATABASE_URL=
APP_SECRET_KEY=
APP_DEBUG=&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 설정 클래스도 접두사를 인식하도록 만들 수 있다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;# app/config.py

from functools import lru_cache

from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
    database_url: str
    secret_key: str
    debug: bool = False

    model_config = SettingsConfigDict(
        env_file=&quot;.env&quot;,
        env_file_encoding=&quot;utf-8&quot;,
        env_prefix=&quot;APP_&quot;,
    )


@lru_cache
def get_settings() -&amp;gt; Settings:
    return Settings()&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;code&gt;.env&lt;/code&gt;의 &lt;code&gt;APP_DATABASE_URL&lt;/code&gt; 값이 &lt;code&gt;database_url&lt;/code&gt; 필드로 들어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 하나만 운영한다면 필수는 아니다.&lt;br /&gt;하지만 여러 앱을 같은 서버에서 실행하거나, 환경변수 이름이 겹칠 가능성이 있다면 접두사를 붙이는 편이 안전하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;16. 초보자가 자주 하는 실수 정리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI에서 &lt;code&gt;.env&lt;/code&gt;를 쓸 때 자주 하는 실수는 비슷하다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;실수&lt;/th&gt;
&lt;th&gt;문제&lt;/th&gt;
&lt;th&gt;해결&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.env&lt;/code&gt;를 Git에 올림&lt;/td&gt;
&lt;td&gt;비밀값 노출&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.gitignore&lt;/code&gt; 추가, 노출된 키 교체&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SECRET_KEY&lt;/code&gt;를 코드에 직접 작성&lt;/td&gt;
&lt;td&gt;배포 후 교체 어려움&lt;/td&gt;
&lt;td&gt;환경변수로 분리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.env.example&lt;/code&gt;을 만들지 않음&lt;/td&gt;
&lt;td&gt;협업 시 필요한 값 파악 어려움&lt;/td&gt;
&lt;td&gt;변수 이름만 담은 예시 파일 작성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;os.getenv()&lt;/code&gt;만 사용&lt;/td&gt;
&lt;td&gt;타입 변환 실수 가능&lt;/td&gt;
&lt;td&gt;Pydantic Settings 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.env&lt;/code&gt; 위치를 잘못 둠&lt;/td&gt;
&lt;td&gt;설정값 로드 실패&lt;/td&gt;
&lt;td&gt;프로젝트 루트 실행 또는 절대 경로 지정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pip install jwt&lt;/code&gt; 실행&lt;/td&gt;
&lt;td&gt;&lt;code&gt;jwt.encode()&lt;/code&gt; 오류 가능&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pip install pyjwt&lt;/code&gt; 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;운영 서버에서 로컬 &lt;code&gt;.env&lt;/code&gt; 그대로 사용&lt;/td&gt;
&lt;td&gt;보안/운영 위험&lt;/td&gt;
&lt;td&gt;서버 환경변수 또는 Secret Manager 검토&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;code&gt;.env&lt;/code&gt; 파일 하나보다 이 체크리스트가 더 중요하다.&lt;br /&gt;설정값을 어디에 두는지보다, 노출되면 어떤 영향을 주는지 먼저 판단해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;전체 코드 예시&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 최소 실행 예시를 한 번에 정리하면 다음과 같다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;code&gt;.env&lt;/code&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;DATABASE_URL=sqlite:///./app.db
SECRET_KEY=local-dev-secret-key
ACCESS_TOKEN_EXPIRE_MINUTES=30
DEBUG=true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;code&gt;.env.example&lt;/code&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;DATABASE_URL=
SECRET_KEY=
ACCESS_TOKEN_EXPIRE_MINUTES=30
DEBUG=false&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;code&gt;.gitignore&lt;/code&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;.env
.venv/
__pycache__/
*.pyc&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;code&gt;requirements.txt&lt;/code&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;fastapi
uvicorn
pydantic-settings
python-dotenv
pyjwt&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;code&gt;app/config.py&lt;/code&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from functools import lru_cache
from pathlib import Path

from pydantic_settings import BaseSettings, SettingsConfigDict

BASE_DIR = Path(__file__).resolve().parent.parent


class Settings(BaseSettings):
    database_url: str
    secret_key: str
    access_token_expire_minutes: int = 30
    debug: bool = False

    model_config = SettingsConfigDict(
        env_file=BASE_DIR / &quot;.env&quot;,
        env_file_encoding=&quot;utf-8&quot;,
    )


@lru_cache
def get_settings() -&amp;gt; Settings:
    return Settings()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;code&gt;app/main.py&lt;/code&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;from fastapi import Depends, FastAPI

from app.config import Settings, get_settings

app = FastAPI()


@app.get(&quot;/&quot;)
def read_root(settings: Settings = Depends(get_settings)):
    return {
        &quot;message&quot;: &quot;FastAPI environment settings example&quot;,
        &quot;database_url&quot;: settings.database_url,
        &quot;debug&quot;: settings.debug,
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;실행 명령어&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;uvicorn app.main:app --reload&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저에서 아래 주소로 접속한다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://127.0.0.1:8000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;마무리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI에서 &lt;code&gt;.env&lt;/code&gt;를 쓰는 목적은 단순히 파일을 하나 더 만드는 것이 아니다.&lt;br /&gt;DB 주소, 비밀키, 토큰 만료 시간처럼 환경마다 달라지는 값을 코드에서 분리하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입문 단계에서는 아래 기준만 지켜도 충분하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 값은 &lt;code&gt;.env&lt;/code&gt;에 둔다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.env&lt;/code&gt;는 Git에 올리지 않는다.&lt;/li&gt;
&lt;li&gt;공유용으로 &lt;code&gt;.env.example&lt;/code&gt;을 만든다.&lt;/li&gt;
&lt;li&gt;설정값은 &lt;code&gt;pydantic-settings&lt;/code&gt;로 읽고 검증한다.&lt;/li&gt;
&lt;li&gt;JWT 예제에서는 &lt;code&gt;pip install pyjwt&lt;/code&gt;를 사용한다.&lt;/li&gt;
&lt;li&gt;비밀키는 API 응답이나 로그에 출력하지 않는다.&lt;/li&gt;
&lt;li&gt;운영 환경에서는 서버 환경변수나 Secret Manager 사용을 검토한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 &lt;code&gt;os.getenv()&lt;/code&gt;로도 충분해 보일 수 있다.&lt;br /&gt;하지만 DB 주소, JWT 비밀키, 디버그 모드, 토큰 만료 시간처럼 설정값이 늘어나면 Pydantic Settings로 관리하는 편이 훨씬 안정적이다.&lt;/p&gt;</description>
      <category>개발/FastAPI</category>
      <category>API보안</category>
      <category>fastapi</category>
      <category>pydantic</category>
      <category>파이썬백엔드</category>
      <category>환경변수</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/61</guid>
      <comments>https://notebase.tistory.com/entry/fastapi-env-database-secret-key#entry61comment</comments>
      <pubDate>Thu, 4 Jun 2026 13:45:34 +0900</pubDate>
    </item>
    <item>
      <title>파이썬 openpyxl 엑셀 자동화 기초: 파일 만들기부터 수정까지</title>
      <link>https://notebase.tistory.com/entry/python-openpyxl-excel-automation-basic</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 openpyxl로 엑셀 파일을 만들고, 읽고, 수정하고, 저장하는 기본 흐름을 초보자 기준으로 정리합니다. 자주 발생하는 오류와 사용 시 주의할 점도 함께 다룹니다.&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;code&gt;openpyxl&lt;/code&gt;을 써볼 만합니다. 파이썬으로 &lt;code&gt;.xlsx&lt;/code&gt; 파일을 만들고, 읽고, 수정하고, 다시 저장할 수 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 처음부터 &amp;ldquo;회사 업무 전체를 자동화하겠다&amp;rdquo;는 식으로 접근하면 금방 막힙니다. 먼저 엑셀 파일 구조를 파이썬 코드로 어떻게 다루는지 이해하는 게 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 openpyxl을 처음 쓰는 사람을 기준으로, 가장 기본이 되는 흐름만 정리합니다.&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;li&gt;자주 발생하는 오류 확인하기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;openpyxl은 버전에 따라 일부 사용법이 달라질 수 있습니다. 이 글은 2026년 6월 기준으로 공개된 openpyxl 문서와 PyPI 정보를 바탕으로 작성했습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;openpyxl은 언제 쓰면 좋을까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;openpyxl&lt;/code&gt;은 파이썬에서 엑셀 파일을 다룰 때 많이 쓰는 라이브러리입니다. 특히 &lt;code&gt;.xlsx&lt;/code&gt; 파일을 직접 만들거나 수정할 때 유용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 이런 작업에 사용할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;매일 같은 양식의 엑셀 보고서 만들기&lt;/li&gt;
&lt;li&gt;기존 엑셀 파일에서 특정 셀 값 읽기&lt;/li&gt;
&lt;li&gt;여러 행의 데이터를 자동으로 추가하기&lt;/li&gt;
&lt;li&gt;엑셀 파일의 시트 이름, 셀 값, 간단한 스타일 수정하기&lt;/li&gt;
&lt;li&gt;반복적인 정산표, 재고표, 목록 파일 생성하기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점은 openpyxl이 &amp;ldquo;엑셀 프로그램을 대신 클릭해주는 도구&amp;rdquo;는 아니라는 점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엑셀 앱을 자동으로 실행해서 마우스로 조작하는 방식이 아니라, &lt;code&gt;.xlsx&lt;/code&gt; 파일 자체를 파이썬 코드로 읽고 쓰는 방식에 가깝습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 단순한 반복 파일 작업에는 잘 맞지만, 엑셀 화면에서 버튼을 누르거나 매크로를 실행하는 자동화와는 성격이 다릅니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;openpyxl 설치하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 터미널에서 openpyxl을 설치합니다.&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;pip install openpyxl&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;예를 들어 macOS나 Linux에서는 다음처럼 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;python -m venv .venv
source .venv/bin/activate
pip install openpyxl&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;Windows PowerShell에서는 보통 다음 흐름을 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;python -m venv .venv
.venv\Scripts\Activate.ps1
pip install openpyxl&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;설치가 잘 되었는지 확인하려면 파이썬에서 import 해보면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;import openpyxl

print(openpyxl.__version__)&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;openpyxl의 기본 구조 이해하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;openpyxl을 쓰기 전에 엑셀 파일 구조를 간단히 정리하면 이해가 쉬워집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엑셀 파일은 대략 이런 구조입니다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;엑셀 파일 Workbook
 └── 시트 Worksheet
      └── 셀 Cell&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;code&gt;sales.xlsx&lt;/code&gt;라는 파일 안에 &lt;code&gt;1월매출&lt;/code&gt;, &lt;code&gt;2월매출&lt;/code&gt; 같은 시트가 있고, 각 시트 안에 &lt;code&gt;A1&lt;/code&gt;, &lt;code&gt;B2&lt;/code&gt; 같은 셀이 있는 구조입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;openpyxl 코드에서도 이 구조를 그대로 다룹니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Workbook&lt;/code&gt;: 엑셀 파일 전체&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Worksheet&lt;/code&gt;: 엑셀 파일 안의 시트&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Cell&lt;/code&gt;: 시트 안의 개별 셀&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조를 알고 있으면 코드가 훨씬 덜 낯설게 보입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;새 엑셀 파일 만들기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 새 엑셀 파일을 만들어보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일명은 &lt;code&gt;create_excel.py&lt;/code&gt;로 작성합니다.&lt;/p&gt;
&lt;pre class=&quot;vala&quot;&gt;&lt;code&gt;# create_excel.py

from openpyxl import Workbook

# 새 엑셀 파일 생성
workbook = Workbook()

# 현재 활성화된 시트 선택
sheet = workbook.active

# 시트 이름 변경
sheet.title = &quot;매출현황&quot;

# 셀에 값 입력
sheet[&quot;A1&quot;] = &quot;상품명&quot;
sheet[&quot;B1&quot;] = &quot;수량&quot;
sheet[&quot;C1&quot;] = &quot;단가&quot;

sheet[&quot;A2&quot;] = &quot;키보드&quot;
sheet[&quot;B2&quot;] = 3
sheet[&quot;C2&quot;] = 50000

sheet[&quot;A3&quot;] = &quot;마우스&quot;
sheet[&quot;B3&quot;] = 5
sheet[&quot;C3&quot;] = 20000

# 엑셀 파일 저장
workbook.save(&quot;sales_report.xlsx&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;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;python create_excel.py&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;code&gt;sales_report.xlsx&lt;/code&gt; 파일이 만들어졌다면 성공입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로는 여기서부터 엑셀 자동화가 시작됩니다. 사람이 직접 엑셀을 열고 표를 만드는 대신, 파이썬 코드가 정해진 형식으로 파일을 만들어주는 방식입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;셀 값 입력 방식: A1 방식과 row/column 방식&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;openpyxl에서는 셀을 지정하는 방법이 크게 두 가지입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째는 엑셀에서 익숙한 &lt;code&gt;A1&lt;/code&gt;, &lt;code&gt;B2&lt;/code&gt; 방식입니다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;sheet[&quot;A1&quot;] = &quot;이름&quot;
sheet[&quot;B1&quot;] = &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;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;sheet.cell(row=1, column=1).value = &quot;이름&quot;
sheet.cell(row=1, column=2).value = &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;code&gt;A1&lt;/code&gt; 방식이 더 직관적입니다. 하지만 반복문을 사용할 때는 &lt;code&gt;row&lt;/code&gt;, &lt;code&gt;column&lt;/code&gt; 방식이 더 편합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 여러 명의 점수를 한 번에 입력할 때는 다음처럼 작성할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;# write_rows.py

from openpyxl import Workbook

workbook = Workbook()
sheet = workbook.active
sheet.title = &quot;점수표&quot;

students = [
    [&quot;이름&quot;, &quot;국어&quot;, &quot;영어&quot;, &quot;수학&quot;],
    [&quot;김민수&quot;, 90, 85, 88],
    [&quot;이서연&quot;, 78, 92, 81],
    [&quot;박지훈&quot;, 88, 76, 95],
]

for row_index, row_data in enumerate(students, start=1):
    for column_index, value in enumerate(row_data, start=1):
        sheet.cell(row=row_index, column=column_index).value = value

workbook.save(&quot;score.xlsx&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;code&gt;enumerate(..., start=1)&lt;/code&gt;을 사용한 이유는 엑셀의 행과 열 번호가 1부터 시작하기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 리스트 인덱스는 0부터 시작하지만, 엑셀의 첫 번째 행은 1행이고 첫 번째 열은 A열입니다. 이 부분을 헷갈리면 값이 예상과 다른 위치에 들어갈 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;기존 엑셀 파일 읽기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 이미 존재하는 엑셀 파일을 읽어보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에서 만든 &lt;code&gt;sales_report.xlsx&lt;/code&gt; 파일을 기준으로 설명합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일명은 &lt;code&gt;read_excel.py&lt;/code&gt;로 작성합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# read_excel.py

from openpyxl import load_workbook

workbook = load_workbook(&quot;sales_report.xlsx&quot;)
sheet = workbook[&quot;매출현황&quot;]

print(sheet[&quot;A1&quot;].value)
print(sheet[&quot;B2&quot;].value)
print(sheet[&quot;C3&quot;].value)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행합니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;python read_excel.py&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예상 출력은 다음과 비슷합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;상품명
3
20000&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;code&gt;Workbook()&lt;/code&gt;이 아니라 &lt;code&gt;load_workbook()&lt;/code&gt;을 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분을 자주 헷갈립니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;새 파일을 만들 때: &lt;code&gt;Workbook()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;기존 파일을 읽을 때: &lt;code&gt;load_workbook(&quot;파일명.xlsx&quot;)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;기존 엑셀 파일 수정하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 파일을 읽은 뒤 특정 값을 수정하고 다시 저장할 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;# update_excel.py

from openpyxl import load_workbook

workbook = load_workbook(&quot;sales_report.xlsx&quot;)
sheet = workbook[&quot;매출현황&quot;]

# 기존 값 수정
sheet[&quot;B2&quot;] = 10

# 새 행 추가
sheet.append([&quot;모니터&quot;, 2, 180000])

# 다른 이름으로 저장
workbook.save(&quot;sales_report_updated.xlsx&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;code&gt;sheet.append()&lt;/code&gt;는 시트의 마지막 행 다음에 데이터를 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무에서는 원본 파일을 바로 덮어쓰기보다, 처음에는 다른 이름으로 저장하는 편이 안전합니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;workbook.save(&quot;sales_report_updated.xlsx&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;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;여러 행을 읽는 방법&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엑셀 자동화에서는 특정 셀 하나만 읽는 경우보다 여러 행을 반복해서 읽는 경우가 더 많습니다.&lt;/p&gt;
&lt;pre class=&quot;nix&quot;&gt;&lt;code&gt;# read_rows.py

from openpyxl import load_workbook

workbook = load_workbook(&quot;sales_report.xlsx&quot;)
sheet = workbook[&quot;매출현황&quot;]

for row in sheet.iter_rows(min_row=2, values_only=True):
    product_name = row[0]
    quantity = row[1]
    price = row[2]

    print(product_name, quantity, price)&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;code&gt;min_row=2&lt;/code&gt;를 사용한 이유는 첫 번째 행이 제목 행이기 때문입니다.&lt;/p&gt;
&lt;pre class=&quot;gherkin&quot;&gt;&lt;code&gt;상품명 | 수량 | 단가&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 헤더 행까지 데이터로 처리하면 계산이나 저장 과정에서 오류가 생길 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;values_only=True&lt;/code&gt;를 사용하면 셀 객체가 아니라 셀 안의 값만 가져옵니다. 초보자 입장에서는 이 옵션을 켜고 시작하는 편이 이해하기 쉽습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;간단한 합계 열 추가하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 수량과 단가를 곱해서 합계 열을 만들어보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;# add_total.py

from openpyxl import load_workbook

workbook = load_workbook(&quot;sales_report.xlsx&quot;)
sheet = workbook[&quot;매출현황&quot;]

# 헤더 추가
sheet[&quot;D1&quot;] = &quot;합계&quot;

# 2행부터 마지막 행까지 반복
for row_number in range(2, sheet.max_row + 1):
    quantity = sheet[f&quot;B{row_number}&quot;].value
    price = sheet[f&quot;C{row_number}&quot;].value

    total = quantity * price
    sheet[f&quot;D{row_number}&quot;] = total

workbook.save(&quot;sales_report_with_total.xlsx&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;code&gt;sheet.max_row&lt;/code&gt;는 현재 시트에서 사용 중인 마지막 행 번호를 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예시는 단순하지만 실제 업무 자동화에 꽤 자주 쓰이는 패턴입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 파일 읽기&lt;/li&gt;
&lt;li&gt;특정 열 값 가져오기&lt;/li&gt;
&lt;li&gt;계산하기&lt;/li&gt;
&lt;li&gt;새 열에 결과 입력하기&lt;/li&gt;
&lt;li&gt;새 파일로 저장하기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 흐름만 익혀도 반복적인 엑셀 작업을 꽤 많이 줄일 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;엑셀 수식 입력하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;openpyxl로 엑셀 수식도 입력할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;# add_formula.py

from openpyxl import load_workbook

workbook = load_workbook(&quot;sales_report.xlsx&quot;)
sheet = workbook[&quot;매출현황&quot;]

sheet[&quot;D1&quot;] = &quot;합계&quot;
sheet[&quot;D2&quot;] = &quot;=B2*C2&quot;
sheet[&quot;D3&quot;] = &quot;=B3*C3&quot;

workbook.save(&quot;sales_report_formula.xlsx&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;openpyxl은 수식을 &amp;ldquo;입력&amp;rdquo;할 수는 있지만, 엑셀처럼 수식을 직접 계산하는 도구는 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &lt;code&gt;=B2*C2&lt;/code&gt;라는 수식을 파일에 넣을 수는 있지만, 계산 결과를 바로 파이썬에서 기대하면 안 됩니다. 수식 계산은 보통 엑셀 프로그램에서 파일을 열 때 처리됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분을 오해하기 쉽습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계산 결과가 파이썬 코드 안에서 바로 필요하다면 수식 문자열을 넣기보다, 앞에서 본 것처럼 파이썬에서 직접 계산해서 값을 넣는 방식이 더 단순합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;자주 발생하는 오류와 해결 방법&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. ModuleNotFoundError: No module named 'openpyxl'&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;ModuleNotFoundError: No module named 'openpyxl'&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;openpyxl이 설치되지 않았거나, 설치한 파이썬 환경과 실행 중인 파이썬 환경이 다른 경우입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 다음 명령어로 설치합니다.&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;pip install openpyxl&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 안 된다면 현재 사용하는 파이썬에 직접 설치합니다.&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;python -m pip install openpyxl&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. FileNotFoundError: 파일을 찾을 수 없음&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;FileNotFoundError: [Errno 2] No such file or directory: 'sales_report.xlsx'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 오류는 코드에서 지정한 엑셀 파일이 현재 실행 위치에 없을 때 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 코드에서는 이렇게 작성했는데,&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;load_workbook(&quot;sales_report.xlsx&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결 방법은 두 가지입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째는 파이썬 파일과 엑셀 파일을 같은 폴더에 두는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째는 파일 경로를 정확히 지정하는 것입니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;load_workbook(&quot;data/sales_report.xlsx&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;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. KeyError: Worksheet does not exist&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;KeyError: 'Worksheet 매출현황 does not exist.'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엑셀 파일 안에 해당 이름의 시트가 없을 때 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 코드에서 이렇게 작성했는데,&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;sheet = workbook[&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;code&gt;Sheet&lt;/code&gt;, &lt;code&gt;매출 현황&lt;/code&gt;, &lt;code&gt;매출현황 &lt;/code&gt;처럼 다르면 오류가 납니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시트 이름을 먼저 확인해보면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;print(workbook.sheetnames)&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. PermissionError: 파일 권한 오류&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;PermissionError: [Errno 13] Permission denied: 'sales_report.xlsx'&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;Windows 환경에서 자주 볼 수 있는 오류입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분 엑셀 파일을 Microsoft Excel에서 열어둔 상태로 파이썬에서 저장하려고 할 때 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결 방법은 간단합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엑셀 프로그램에서 해당 파일을 닫고 다시 실행하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영 자동화에서는 이 문제를 막기 위해 원본 파일을 직접 덮어쓰기보다 결과 파일을 새 이름으로 저장하는 방식이 더 안전합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;openpyxl을 쓸 때 알아두면 좋은 제한사항&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;openpyxl은 편리하지만 모든 엑셀 작업에 적합한 도구는 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 상황에서는 주의가 필요합니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;상황&lt;/th&gt;
&lt;th&gt;주의할 점&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.xls&lt;/code&gt; 파일 처리&lt;/td&gt;
&lt;td&gt;openpyxl은 주로 &lt;code&gt;.xlsx&lt;/code&gt; 계열 파일에 사용한다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;복잡한 엑셀 수식 계산&lt;/td&gt;
&lt;td&gt;수식 입력은 가능하지만 계산 엔진은 아니다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;엑셀 화면 조작&lt;/td&gt;
&lt;td&gt;엑셀 앱을 직접 클릭하거나 조작하는 도구는 아니다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;매우 큰 파일 처리&lt;/td&gt;
&lt;td&gt;파일 크기가 크면 속도와 메모리 사용량을 고려해야 한다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;매크로 실행&lt;/td&gt;
&lt;td&gt;매크로 실행 자동화 도구로 보면 안 된다&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 &lt;code&gt;.xls&lt;/code&gt;와 &lt;code&gt;.xlsx&lt;/code&gt; 차이를 헷갈리는 경우가 많습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요즘 엑셀 파일은 대부분 &lt;code&gt;.xlsx&lt;/code&gt;지만, 오래된 파일은 &lt;code&gt;.xls&lt;/code&gt;일 수 있습니다. 파일 확장자가 &lt;code&gt;.xls&lt;/code&gt;라면 openpyxl로 바로 처리하기 어렵기 때문에 파일 형식을 먼저 확인해야 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;실습 예제: 매출표 자동 생성하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 간단한 실습 예제를 하나로 정리해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목표는 상품명, 수량, 단가 데이터를 이용해서 엑셀 매출표를 자동 생성하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일명은 &lt;code&gt;sales_automation.py&lt;/code&gt;로 작성합니다.&lt;/p&gt;
&lt;pre class=&quot;nix&quot;&gt;&lt;code&gt;# sales_automation.py

from openpyxl import Workbook
from openpyxl.styles import Font

sales_data = [
    [&quot;키보드&quot;, 3, 50000],
    [&quot;마우스&quot;, 5, 20000],
    [&quot;모니터&quot;, 2, 180000],
    [&quot;노트북 거치대&quot;, 4, 35000],
]

workbook = Workbook()
sheet = workbook.active
sheet.title = &quot;매출현황&quot;

# 헤더 작성
headers = [&quot;상품명&quot;, &quot;수량&quot;, &quot;단가&quot;, &quot;합계&quot;]
sheet.append(headers)

# 헤더 글씨 굵게
for cell in sheet[1]:
    cell.font = Font(bold=True)

# 데이터 작성
for item in sales_data:
    product_name = item[0]
    quantity = item[1]
    price = item[2]
    total = quantity * price

    sheet.append([product_name, quantity, price, total])

# 열 너비 조정
sheet.column_dimensions[&quot;A&quot;].width = 18
sheet.column_dimensions[&quot;B&quot;].width = 10
sheet.column_dimensions[&quot;C&quot;].width = 12
sheet.column_dimensions[&quot;D&quot;].width = 12

workbook.save(&quot;monthly_sales.xlsx&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;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;python sales_automation.py&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;code&gt;monthly_sales.xlsx&lt;/code&gt; 파일이 생성됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 &lt;code&gt;Font(bold=True)&lt;/code&gt;는 첫 번째 행의 제목 글씨를 굵게 만드는 역할을 합니다. &lt;code&gt;column_dimensions[&quot;A&quot;].width&lt;/code&gt;처럼 열 너비를 지정하면 엑셀 파일을 열었을 때 값이 잘리지 않아 결과물을 확인하기 편합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 서식 자동화는 처음부터 많이 넣기보다, 데이터 입력과 저장 흐름을 먼저 익힌 뒤 필요한 만큼만 추가하는 편이 좋습니다. 엑셀 자동화의 핵심은 화려한 디자인보다 정확한 데이터를 반복해서 안정적으로 넣는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;442&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvO23l/dJMcaaestIZ/wKThqYvgvjGpC3ShKePwYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvO23l/dJMcaaestIZ/wKThqYvgvjGpC3ShKePwYK/img.png&quot; data-alt=&quot;sales_automation.py 실행결과인 monthly_sales.xlsx 파일&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvO23l/dJMcaaestIZ/wKThqYvgvjGpC3ShKePwYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvO23l%2FdJMcaaestIZ%2FwKThqYvgvjGpC3ShKePwYK%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;674&quot; height=&quot;442&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;442&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;sales_automation.py 실행결과인 monthly_sales.xlsx 파일&lt;/figcaption&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;code&gt;sales_automation.py&lt;/code&gt;를 실행한 뒤 생성된 &lt;code&gt;monthly_sales.xlsx&lt;/code&gt; 파일을 열어 캡처하면 됩니다. 상품명, 수량, 단가, 합계가 표 형태로 들어간 화면을 보여주면 독자가 코드 실행 결과를 바로 이해할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예제는 단순하지만 엑셀 자동화의 기본 흐름을 대부분 포함합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터를 리스트로 준비한다&lt;/li&gt;
&lt;li&gt;새 엑셀 파일을 만든다&lt;/li&gt;
&lt;li&gt;헤더를 작성한다&lt;/li&gt;
&lt;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;실제로는 &lt;code&gt;sales_data&lt;/code&gt; 부분을 데이터베이스, CSV 파일, API 응답, 크롤링 결과 등으로 바꿔서 사용할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;openpyxl 기초 학습 순서&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;openpyxl을 처음 배운다면 모든 기능을 한 번에 보려고 하지 않는 편이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 순서로 익히면 덜 막힙니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&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;li&gt;기존 파일 수정 후 다른 이름으로 저장하기&lt;/li&gt;
&lt;li&gt;간단한 스타일 적용하기&lt;/li&gt;
&lt;li&gt;실제 반복 업무 파일에 적용하기&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 차트, 이미지, 병합 셀, 복잡한 스타일을 다루면 코드가 금방 복잡해집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 &amp;ldquo;값을 읽고 쓰는 자동화&amp;rdquo;부터 익히는 것이 좋습니다. 대부분의 업무 자동화는 화려한 서식보다 정확한 데이터 입력과 저장이 더 중요합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;마무리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;openpyxl은 파이썬으로 엑셀 파일을 다룰 때 입문자가 접근하기 좋은 라이브러리입니다. 새 파일을 만들고, 기존 파일을 읽고, 셀 값을 수정하고, 반복문으로 여러 행을 처리하는 기본 흐름만 익혀도 반복적인 엑셀 작업을 줄일 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 openpyxl을 엑셀 프로그램 자체의 대체재로 보면 안 됩니다. 수식 계산, 매크로 실행, 화면 조작까지 모두 처리하는 도구가 아니라 &lt;code&gt;.xlsx&lt;/code&gt; 파일을 코드로 읽고 쓰는 도구에 가깝습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 원본 파일을 바로 덮어쓰지 말고, 항상 다른 이름으로 저장하면서 연습하는 것이 안전합니다. 그다음 자주 반복하는 보고서나 정리 파일 하나를 골라 작은 자동화부터 적용해보면 됩니다.&lt;/p&gt;</description>
      <category>개발/python</category>
      <category>openpyxl</category>
      <category>업무자동화</category>
      <category>엑셀자동화</category>
      <category>파이썬기초</category>
      <category>파이썬실습</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/59</guid>
      <comments>https://notebase.tistory.com/entry/python-openpyxl-excel-automation-basic#entry59comment</comments>
      <pubDate>Tue, 2 Jun 2026 09:11:57 +0900</pubDate>
    </item>
    <item>
      <title>Gemini CLI 종료? Antigravity CLI로 바뀌는 이유 정리</title>
      <link>https://notebase.tistory.com/entry/gemini-cli-antigravity-cli-transition</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Gemini CLI 종료 이슈와 Antigravity CLI 전환 이유를 정리했습니다. 개인&amp;middot;기업용 차이와 기존 사용자의 점검 사항도 살펴봅니다.&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;Gemini CLI를 쓰고 있었다면 가장 먼저 확인해야 할 점은 하나다.&lt;br /&gt;이 변화는 단순한 이름 변경이 아니라, Google이 터미널용 AI 코딩 도구를 &lt;b&gt;Antigravity 플랫폼 안으로 통합하는 전환&lt;/b&gt;에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인 사용자 기준으로는 Gemini CLI를 계속 그대로 쓰기 어렵다. Google은 Gemini CLI와 Gemini Code Assist IDE 확장이 개인 사용자, Google AI Pro, Google AI Ultra 사용자 대상으로 요청 처리를 중단한다고 안내했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 모든 사용자가 한 번에 끊기는 것은 아니다.&lt;br /&gt;기업용 라이선스나 Google Cloud 기반으로 사용하는 경우에는 조건이 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Gemini CLI는 정말 종료되는 걸까&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정확히 말하면, 개인 사용자가 쓰던 Gemini CLI는 Antigravity CLI로 넘어가는 흐름이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Google은 Gemini CLI를 Antigravity CLI로 전환한다고 공식 발표했다. 대상에는 개인용 Gemini Code Assist를 통해 무료로 사용하던 사용자, Google AI Pro, Google AI Ultra 사용자가 포함된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영향을 받는 항목은 크게 두 가지다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Gemini CLI&lt;/li&gt;
&lt;li&gt;Gemini Code Assist IDE 확장&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 터미널에서 Gemini CLI를 쓰던 사람뿐 아니라 VS Code 같은 IDE에서 Gemini Code Assist 확장을 사용하던 사람도 영향을 받을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 기업 사용자는 조금 다르다.&lt;br /&gt;Gemini Code Assist Standard 또는 Enterprise 라이선스를 사용하는 조직, Google Cloud를 통해 Gemini Code Assist for GitHub를 사용하는 조직은 기존 접근이 유지된다고 안내되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 &amp;ldquo;Gemini CLI가 완전히 사라진다&amp;rdquo;라고만 말하면 조금 부정확하다.&lt;br /&gt;개인 사용자 중심의 기존 Gemini CLI 흐름은 Antigravity CLI로 전환되고, 기업용 경로는 별도로 유지되는 구조에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Gemini CLI에서 Antigravity CLI로 바뀌는 이유&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;표면적으로는 제품명 변경처럼 보이지만, Google이 설명한 핵심 이유는 개발 워크플로우의 변화다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gemini CLI는 터미널에서 Gemini 모델을 바로 호출할 수 있는 가벼운 도구였다. 간단한 코드 질문, 파일 수정, 프로젝트 생성, 명령 실행 보조에는 잘 맞았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 AI 코딩 도구의 방향이 바뀌고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에는 &amp;ldquo;터미널에서 모델에게 물어보고 답을 받는 방식&amp;rdquo;이 중심이었다면, 지금은 여러 에이전트가 나눠서 작업하고, 파일을 수정하고, 명령을 실행하고, 긴 작업을 백그라운드에서 처리하는 흐름으로 이동하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Google이 Antigravity를 강조하는 이유도 여기에 있다.&lt;br /&gt;Antigravity는 단순 CLI 하나가 아니라, 데스크톱 앱과 CLI가 같은 에이전트 기반 구조를 공유하는 개발 플랫폼에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점은 Gemini CLI가 실패해서 사라진다기보다, Google이 AI 코딩 도구의 중심을 &amp;ldquo;단일 터미널 도구&amp;rdquo;에서 &amp;ldquo;에이전트 플랫폼&amp;rdquo;으로 옮기고 있다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Antigravity CLI는 Gemini CLI와 무엇이 다른가&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Antigravity CLI는 터미널에서 쓰는 AI 코딩 도구라는 점에서는 Gemini CLI와 비슷하다.&lt;br /&gt;하지만 방향은 조금 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gemini CLI가 비교적 가볍고 직접적인 터미널 인터페이스였다면, Antigravity CLI는 Antigravity 2.0과 같은 에이전트 엔진을 공유하는 CLI다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 자료 기준으로 Antigravity CLI는 다음 흐름을 강조한다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;Gemini CLI&lt;/th&gt;
&lt;th&gt;Antigravity CLI&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;아키텍처 성격&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;터미널 중심 AI 도구&lt;/td&gt;
&lt;td&gt;Antigravity 플랫폼 연계형 CLI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;핵심 워크플로우&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;모델 호출, 코드 보조, 명령 실행&lt;/td&gt;
&lt;td&gt;멀티 에이전트, 백그라운드 작업, 통합 에이전트 구조&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;확장 방식&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Extensions 계열&lt;/td&gt;
&lt;td&gt;Antigravity plugins 중심&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;연동 범위&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;로컬 터미널 중심&lt;/td&gt;
&lt;td&gt;터미널, SSH, Antigravity 2.0 연계&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;전략적 방향성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;가벼운 단독 CLI 도구&lt;/td&gt;
&lt;td&gt;에이전트 기반 개발 플랫폼의 일부&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Google은 Antigravity CLI가 Gemini CLI의 핵심 기능을 일부 유지한다고 설명한다.&lt;br /&gt;대표적으로 Agent Skills, Hooks, Subagents, Extensions 계열 기능이 Antigravity 쪽으로 이어진다. 다만 처음부터 1:1 기능 동등성이 보장되는 것은 아니라고 밝히고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분은 기존 Gemini CLI 사용자에게 꽤 중요하다.&lt;br /&gt;명령어, 설정 방식, 확장 구조가 완전히 동일하게 유지된다고 기대하면 안 된다. 특히 자동화 스크립트나 팀 내부 문서에 Gemini CLI 명령어를 넣어둔 경우에는 전환 테스트가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;기존 Gemini CLI 사용자는 무엇을 해야 하나&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gemini CLI를 개인 계정으로 사용 중이라면 Antigravity CLI 설치와 로그인 흐름을 미리 확인하는 편이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Antigravity CLI는 macOS, Linux, Windows 설치 방식을 제공한다. 터미널 사용자라면 기존 CLI를 대체할 수 있는지 먼저 간단한 프로젝트에서 확인해보는 것이 안전하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 아래에 해당하면 바로 점검하는 게 좋다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Gemini CLI를 매일 터미널에서 사용한다&lt;/li&gt;
&lt;li&gt;프로젝트 생성, 코드 수정, 리팩터링에 Gemini CLI를 활용한다&lt;/li&gt;
&lt;li&gt;VS Code에서 Gemini Code Assist 확장을 사용한다&lt;/li&gt;
&lt;li&gt;사내 문서나 개인 자동화 스크립트에 Gemini CLI 명령어가 들어 있다&lt;/li&gt;
&lt;li&gt;MCP, Hooks, 확장 기능을 연결해서 쓰고 있다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Antigravity CLI를 설치한 뒤에는 공식 문서의 기본 실행 예시를 먼저 확인하는 것이 좋다.&lt;br /&gt;새로 전환되는 도구인 만큼 명령어, 옵션, 인증 방식이 업데이트될 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그나 커뮤니티에 올라온 예시 명령어를 그대로 따라 하기보다는, 공식 문서의 설치 및 시작하기 문서를 기준으로 확인하는 편이 안전하다. CLI 도구는 설치 과정에서 터미널 명령을 실행하는 경우가 많고, AI 코딩 도구는 프로젝트 파일과 명령 실행 권한을 함께 다루기 때문이다.&lt;/p&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;AI 코딩 도구는 단순 앱이 아니라 개발 환경의 일부가 되는 경우가 많다. 그래서 &amp;ldquo;설치만 바꾸면 끝&amp;rdquo;이라고 보기 어렵다. 설정 파일, 권한, 플러그인, 인증 방식, 기존 워크플로우까지 같이 확인해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;VS Code 확장 사용자는 어떻게 봐야 하나&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 변화에서 헷갈리기 쉬운 부분이 IDE 확장이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gemini CLI만 쓰던 사람은 &amp;ldquo;터미널 도구만 바뀌는구나&amp;rdquo;라고 이해하면 되지만, Google 발표에는 Gemini Code Assist IDE 확장도 포함되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인 사용자 또는 Google AI Pro, Google AI Ultra 사용자가 Gemini Code Assist IDE 확장을 사용 중이라면, 해당 확장의 요청 처리 중단 대상에 들어간다.&lt;br /&gt;반면 기업용 Gemini Code Assist Standard, Enterprise 라이선스를 쓰는 조직은 기존 접근이 유지된다고 안내되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, VS Code 확장을 쓰는 사람은 본인이 어떤 계정 유형으로 사용 중인지 확인해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인 계정으로 쓰고 있다면 Antigravity 쪽 전환을 준비하는 것이 맞다.&lt;br /&gt;회사 계정으로 쓰고 있다면 조직 라이선스 정책을 먼저 확인해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 무작정 확장을 지우거나 설정을 바꾸기보다는, 현재 로그인 계정과 라이선스 유형을 먼저 확인하는 편이 안전하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Antigravity CLI로 넘어갈 때 주의할 점&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Antigravity CLI는 Gemini CLI보다 더 강한 에이전트형 도구를 지향한다.&lt;br /&gt;이 말은 편해진다는 뜻이기도 하지만, 동시에 권한 관리가 더 중요해진다는 뜻이기도 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 저장소에서도 AI 코딩 에이전트 사용 시 자율 코드 실행, 데이터 유출, 프롬프트 인젝션, 공급망 위험 같은 보안 이슈를 주의하라고 안내한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로는 아래 정도는 확인하고 쓰는 게 좋다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 설치 경로는 공식 문서에서 확인하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 개발 도구는 공격자들이 사칭하기 좋은 대상이다.&lt;br /&gt;특히 CLI 도구는 설치 과정에서 터미널 명령어를 실행하는 경우가 많기 때문에, 블로그나 커뮤니티에 있는 설치 명령어를 그대로 복사하기보다 공식 문서에서 확인하는 습관이 필요하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 프로젝트 루트에서 바로 실행하기 전에 테스트하기&lt;/b&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;AI 에이전트는 파일을 읽고, 수정하고, 명령을 실행할 수 있다.&lt;br /&gt;도구가 똑똑해질수록 실수했을 때의 영향도 커진다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. Git 상태를 깨끗하게 만든 뒤 사용하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 도구를 실행하기 전에는 최소한 현재 변경사항을 커밋하거나 별도 브랜치를 만드는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 이런 흐름이 안전하다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;git status
git checkout -b test/antigravity-cli&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업 후에는 변경된 파일을 직접 확인해야 한다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;git diff&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;AI가 만든 코드가 그럴듯해 보여도, 실제 요구사항과 보안 기준을 만족하는지는 사람이 확인해야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. 플러그인과 권한 설정 확인하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Antigravity CLI에서는 기존 Extensions 흐름이 Antigravity plugins 쪽으로 이어진다.&lt;br /&gt;플러그인은 편리하지만, 외부 도구 연결이나 명령 실행과 관련될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 필요한 기능만 최소한으로 연결하는 편이 좋다.&lt;br /&gt;특히 MCP 서버, 훅, 서브에이전트 같은 기능을 연결할 때는 어떤 파일에 접근하는지, 어떤 명령을 실행할 수 있는지 확인해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;왜 Google은 굳이 Antigravity로 통합하려 할까&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자 입장에서는 불편하게 느껴질 수 있다.&lt;br /&gt;Gemini CLI가 나온 지 오래되지 않았고, 이미 익숙해진 사용자도 많기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Google 입장에서는 Gemini CLI, Gemini Code Assist, Antigravity 2.0, GitHub 연동, 기업용 AI 개발 도구를 따로 발전시키는 것보다 하나의 에이전트 기반 플랫폼으로 묶는 편이 유리하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 도구 시장은 단순 자동완성 경쟁에서 벗어나고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 경쟁 포인트는 &amp;ldquo;코드를 얼마나 잘 추천하느냐&amp;rdquo;만이 아니다.&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;터미널과 IDE, 데스크톱 앱이 같은 맥락을 공유하는가&lt;/li&gt;
&lt;li&gt;팀 단위 권한과 보안을 관리할 수 있는가&lt;/li&gt;
&lt;li&gt;에이전트가 작업한 내역을 추적할 수 있는가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 요소가 중요해지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gemini CLI는 터미널 도구로는 매력적이었지만, Google이 그리는 방향은 더 넓은 개발 플랫폼이다.&lt;br /&gt;그래서 Antigravity CLI는 Gemini CLI의 후속 도구이면서, 동시에 Antigravity 생태계로 사용자를 옮기는 입구 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Gemini CLI 사용자는 바로 갈아타야 할까&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인 사용자라면 미리 갈아타는 쪽이 현실적이다.&lt;br /&gt;기존 Gemini CLI가 계속 같은 방식으로 유지될 가능성은 낮아 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 당장 모든 작업을 Antigravity CLI에 맡길 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 아래 순서로 확인하는 것이 좋다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Antigravity CLI 설치&lt;/li&gt;
&lt;li&gt;로그인 및 기본 명령 확인&lt;/li&gt;
&lt;li&gt;작은 테스트 프로젝트에서 파일 수정 테스트&lt;/li&gt;
&lt;li&gt;기존 Gemini CLI에서 자주 쓰던 명령과 비교&lt;/li&gt;
&lt;li&gt;플러그인, Hooks, MCP 연결 여부 확인&lt;/li&gt;
&lt;li&gt;실제 프로젝트에는 별도 브랜치에서 적용&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 기존에 Gemini CLI를 자동화 스크립트처럼 쓰고 있었다면, 명령어 호환성을 직접 확인해야 한다.&lt;br /&gt;Google이 핵심 기능을 유지한다고 했더라도, 1:1 호환을 보장하는 것은 아니기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;정리: 이름 변경보다 플랫폼 전환에 가깝다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gemini CLI에서 Antigravity CLI로의 변화는 단순 리브랜딩으로 보기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 Google의 AI 코딩 도구 전략이 터미널 단일 도구에서 에이전트 플랫폼 중심으로 이동하고 있다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인 사용자라면 Gemini CLI와 Gemini Code Assist IDE 확장 사용 흐름을 Antigravity CLI 기준으로 다시 확인해야 한다.&lt;br /&gt;기업 사용자는 조직 라이선스에 따라 영향 범위가 다르므로, 개인 계정 기준 안내만 보고 판단하면 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 Gemini CLI를 가볍게 질문용으로만 썼다면 전환 부담은 크지 않을 수 있다.&lt;br /&gt;하지만 코드 수정, 명령 실행, 확장 기능, MCP, 자동화까지 연결해두었다면 미리 테스트해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Antigravity CLI는 더 강한 에이전트형 개발 도구를 지향한다.&lt;br /&gt;그만큼 편해질 수 있지만, 권한과 보안, 변경사항 검토는 더 중요해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금 Gemini CLI를 쓰고 있다면 &amp;ldquo;언제까지 쓸 수 있나&amp;rdquo;보다 &amp;ldquo;내 개발 흐름에서 어떤 부분을 Antigravity CLI로 옮겨야 하나&amp;rdquo;를 먼저 확인하는 편이 낫다.&lt;/p&gt;</description>
      <category>AI/AI 코딩</category>
      <category>AI도구</category>
      <category>AI코딩</category>
      <category>AI코딩에이전트</category>
      <category>Gemini</category>
      <category>개발도구</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/58</guid>
      <comments>https://notebase.tistory.com/entry/gemini-cli-antigravity-cli-transition#entry58comment</comments>
      <pubDate>Mon, 1 Jun 2026 15:09:14 +0900</pubDate>
    </item>
    <item>
      <title>바이브 코딩 실전 체크리스트: 만들기 전에 정해야 할 것들</title>
      <link>https://notebase.tistory.com/entry/vibe-coding-practical-checklist-before-building</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;바이브 코딩을 시작하기 전에 정해야 할 기능, 화면, 데이터, 로그인, 보안, 테스트, 배포 기준을 실전 체크리스트로 정리했습니다. AI 코딩 도구로 프로젝트를 만들기 전 참고하기 좋습니다.&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;바이브 코딩은 AI에게 &amp;ldquo;이런 앱 만들어줘&amp;rdquo;라고 말하는 것만으로 끝나지 않는다. 실제로는 만들기 전에 무엇을 정했는지가 결과물의 품질을 거의 결정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 도구가 좋아지면서 작은 웹앱이나 자동화 도구는 예전보다 훨씬 빠르게 만들 수 있다. Cursor, Claude Code, Codex, Gemini CLI, GitHub Copilot 같은 도구를 쓰면 코드 작성과 수정 속도는 확실히 빨라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 속도다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;너무 빨리 만들어지기 때문에, 처음에 기준을 정하지 않으면 나중에 어디서 꼬였는지 찾기 어려워진다. 기능은 늘어나고, 파일은 많아지고, AI는 이전 의도를 잊은 것처럼 다른 방향으로 코드를 바꾼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 바이브 코딩을 할 때는 &amp;ldquo;코드를 어떻게 짤까?&amp;rdquo;보다 먼저 &amp;ldquo;무엇을 만들고, 어디까지 만들고, 어떤 기준으로 확인할까?&amp;rdquo;를 정해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 바이브 코딩으로 프로젝트를 시작하기 전에 정리해두면 좋은 실전 체크리스트다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;바이브 코딩은 코딩보다 결정이 먼저다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바이브 코딩을 처음 하면 보통 이렇게 시작한다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&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;AI는 바로 코드를 만들어준다. 화면도 생기고, 버튼도 생기고, 어느 정도 동작도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 조금만 더 진행하면 질문이 생긴다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;할 일을 어디에 저장하지?
로그인은 필요한가?
삭제한 데이터는 복구할 수 있어야 하나?
모바일에서도 써야 하나?
혼자 쓰는 앱인가, 여러 명이 쓰는 앱인가?
API 키는 어디에 보관해야 하지?&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;AI는 사용자의 의도를 추측해서 구현한다. 사용자가 정하지 않은 부분은 AI가 알아서 채운다. 이게 편할 때도 있지만, 실전에서는 오히려 문제가 된다.&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;ldquo;기획 없이 개발하는 방식&amp;rdquo;은 아니라는 것이다. 오히려 기획이 짧고 선명해야 AI가 제대로 움직인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1단계: 방향 정하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AI에게 코드를 맡기기 전에, 사람이 먼저 프로젝트의 기준선을 정하는 단계다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 세부 기능을 전부 정할 필요는 없다. 하지만 &amp;ldquo;무엇을 만들고, 누가 쓰고, 어떤 상황에서 쓰는지&amp;rdquo;는 정해두는 편이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 기준이 없으면 AI가 알아서 방향을 잡는다. 문제는 그 방향이 내가 원한 방향과 다를 수 있다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. 무엇을 만들지 한 문장으로 정하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 정할 것은 프로젝트 설명이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;긴 기획서가 아니어도 된다. 대신 한 문장으로 말할 수 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋지 않은 예시는 이런 식이다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&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;너무 넓다. AI가 무엇을 만들어야 할지 추측해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 더 좋은 예시는 이렇다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;티스토리 블로그 글 제목과 메타 설명을 입력하면 SEO 점검표를 만들어주는 웹앱을 만든다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정도면 방향이 훨씬 선명해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 문장 안에는 가능하면 세 가지가 들어가면 좋다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;누가 쓰는지&lt;/li&gt;
&lt;li&gt;무엇을 입력하는지&lt;/li&gt;
&lt;li&gt;어떤 결과를 얻는지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;개발 입문자가 에러 메시지를 붙여넣으면 원인과 확인할 명령어를 알려주는 웹앱을 만든다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 이렇게 정리할 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;개인 사용자가 구독 서비스 이름과 결제일을 입력하면 이번 달 결제 예정 목록을 보여주는 앱을 만든다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 한 문장이 프로젝트의 기준점이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 다른 방향으로 코드를 만들기 시작하면 다시 이 문장으로 돌아오면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. 사용자와 사용 상황 정하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 누가, 언제, 왜 쓰는지 정해야 한다.&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;ldquo;메모 앱&amp;rdquo;을 만든다고 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혼자 쓰는 메모 앱인지, 팀이 같이 쓰는 메모 앱인지에 따라 구조가 달라진다. 혼자 쓰는 앱이면 로그인 없이 로컬 저장만으로도 충분할 수 있다. 팀이 쓰는 앱이면 계정, 권한, 공유 기능이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 메모 앱이어도 사용 상황에 따라 완전히 다른 프로젝트가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리할 때는 거창하게 쓰지 않아도 된다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;사용자: 개발 공부를 시작한 초보자
상황: 에러 메시지를 봤지만 어디서부터 확인해야 할지 모를 때
목적: 에러 원인 후보와 확인 순서를 빠르게 알고 싶다&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 이런 식으로 정리할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&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;이 정도만 정해도 AI에게 훨씬 정확하게 지시할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2단계: 구조 설계하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이 단계에서는 기능, 화면, 데이터, 권한 범위를 정한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바이브 코딩에서 프로젝트가 꼬이는 이유는 대부분 이 구간에서 나온다. 기능을 계속 추가하다가 화면 구조가 복잡해지고, 데이터 구조가 바뀌고, 로그인이나 권한 같은 무거운 기능이 뒤늦게 붙으면서 코드가 흔들린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 목표는 &amp;ldquo;작지만 끝까지 동작하는 버전&amp;rdquo;을 만드는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. 핵심 기능과 나중 기능 나누기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바이브 코딩에서 가장 흔한 실수는 처음부터 기능을 많이 넣는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI는 요청하면 대부분 만들어준다. 그래서 채팅창에 계속 기능을 추가하게 된다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;로그인도 넣어줘.
다크모드도 넣어줘.
관리자 페이지도 넣어줘.
엑셀 다운로드도 넣어줘.
알림 기능도 넣어줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 기능이 늘어나는 속도보다 구조가 망가지는 속도가 더 빠를 수 있다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 핵심 기능만 정해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &amp;ldquo;구독 결제 관리 앱&amp;rdquo;이라면 핵심 기능은 이 정도면 충분하다.&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;반대로 아래 기능은 나중으로 미뤄도 된다.&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;li&gt;가족 공유&lt;/li&gt;
&lt;li&gt;다크모드&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 모든 기능을 넣으려고 하면 AI가 만든 코드도 복잡해진다. 초보자 입장에서는 나중에 수정하기 더 어려워진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 버전은 작아도 된다. 대신 실제로 끝까지 동작해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. 화면 흐름 먼저 그리기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 만들기 전에 화면 흐름을 정해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디자인을 예쁘게 만들라는 뜻이 아니다. 사용자가 어떤 순서로 움직이는지만 정하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 간단한 SEO 점검 도구라면 화면 흐름은 이렇게 잡을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 제목 입력
2. 메타 설명 입력
3. 본문 일부 입력
4. 점검하기 버튼 클릭
5. SEO 체크 결과 표시&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;이 흐름이 없으면 AI는 화면을 마음대로 구성한다. 어떤 경우에는 입력창이 너무 많아지고, 어떤 경우에는 결과 화면이 불필요하게 복잡해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 종이에 써도 되고, 메모장에 적어도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 것은 화면 단위를 먼저 나누는 것이다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&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;이렇게 화면을 나누면 AI에게 작업을 시킬 때도 편하다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;먼저 입력 화면만 만들어줘.
아직 API 연결은 하지 말고, 임시 데이터로 결과 화면까지 확인할 수 있게 해줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 작은 단위로 요청할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;5. 데이터 구조 정하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바이브 코딩에서 데이터 구조를 대충 넘기면 나중에 수정 비용이 커진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 할 일 관리 앱을 만든다고 하면 &amp;ldquo;할 일&amp;rdquo;에 어떤 정보가 들어갈지 먼저 정해야 한다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;할 일
- id
- 제목
- 완료 여부
- 생성일
- 마감일&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 이 정도면 충분하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 우선순위, 태그, 반복 일정, 첨부파일까지 한 번에 넣으면 구조가 복잡해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그 SEO 점검 도구라면 데이터 구조는 이렇게 잡을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;SEO 점검 요청
- 제목
- 메타 설명
- 본문
- 핵심 키워드
- 작성일

SEO 점검 결과
- 제목 길이 평가
- 메타 설명 평가
- 키워드 포함 여부
- 개선 제안 목록&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;이렇게 데이터 구조를 먼저 정하면 AI가 코드를 만들 때 흔들림이 줄어든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 데이터베이스를 쓰는 프로젝트라면 이 단계가 더 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 SQLite, Supabase, Firebase, PostgreSQL 같은 선택지도 고민하게 된다. 하지만 초보 단계에서는 저장 방식보다 데이터 항목을 먼저 정하는 것이 더 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저장소는 나중에 바꿀 수 있지만, 데이터 구조가 계속 바뀌면 전체 코드가 흔들린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;6. 로그인과 권한 범위 정하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인은 생각보다 무거운 기능이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;그냥 로그인 붙여줘&amp;rdquo;라고 하면 AI가 코드를 만들어줄 수는 있다. 하지만 실제 서비스 관점에서는 계정 생성, 비밀번호 저장, 세션, 토큰, 로그아웃, 권한 확인, 개인정보 처리까지 이어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혼자 쓰는 도구라면 처음부터 로그인이 필요 없을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 로컬에서만 쓰는 블로그 제목 점검 도구라면 로그인 없이 시작해도 된다. 반대로 여러 사용자가 각자의 데이터를 저장해야 하는 앱이라면 로그인이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 전에 아래 질문에 답해보면 좋다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;이 앱은 나 혼자 쓰는가?
다른 사용자도 쓰는가?
사용자별로 데이터를 분리해야 하는가?
관리자 권한이 필요한가?
민감한 정보를 저장하는가?
API 키나 토큰이 필요한가?&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;ldquo;민감한 정보&amp;rdquo;가 들어가면 더 조심해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비밀번호, API 키, 결제 정보, 개인 연락처, 업무 문서 같은 데이터는 AI가 만든 코드를 그대로 믿고 저장하면 위험하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API 키나 비밀번호가 필요한 프로젝트라면 처음부터 AI에게 이렇게 요구하는 편이 좋다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;API 키, 비밀번호, 토큰 같은 민감한 값은 코드에 직접 쓰지 말고 .env 환경 변수로 분리해줘.
.env 파일은 Git에 올라가지 않도록 .gitignore에 추가해줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 바이브 코딩 실습이라면 민감한 정보를 다루지 않는 프로젝트로 시작하는 편이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3단계: 실행과 검증 준비하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이 단계에서는 AI에게 어떤 순서로 일을 맡길지, 무엇을 성공 기준으로 볼지 정한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바이브 코딩은 빠르게 만들 수 있다는 장점이 있다. 하지만 빠르게 만든 만큼 검증 기준이 없으면 &amp;ldquo;일단 돌아가는 것처럼 보이는 코드&amp;rdquo;에서 멈추기 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI에게 한 번에 많이 시키기보다, 작게 나누고 확인하면서 진행하는 편이 안전하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;7. AI에게 줄 작업 단위 쪼개기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 도구를 쓸 때는 요청을 작게 나눠야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 번에 이렇게 요청하면 결과가 흔들리기 쉽다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;구독 관리 앱 전체를 만들어줘. 로그인, DB, 통계, 알림, 배포까지 다 해줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;겉보기에는 빠르지만, 실제로는 검토하기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 좋은 방식은 작업을 단계별로 나누는 것이다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1단계: React로 기본 화면만 만들어줘. 데이터는 임시 배열을 사용해.
2단계: 구독 항목 추가와 삭제 기능을 붙여줘.
3단계: localStorage에 저장되게 해줘.
4단계: 월 결제 합계를 계산해줘.
5단계: 코드 구조를 컴포넌트 단위로 정리해줘.&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;AI에게 줄 지시문에는 &amp;ldquo;하지 말아야 할 것&amp;rdquo;도 넣는 것이 좋다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;아직 로그인은 만들지 마.
아직 서버 API는 연결하지 마.
디자인보다 기능 동작을 먼저 확인할 수 있게 해줘.
기존 파일 구조는 유지해줘.
수정한 파일 목록을 마지막에 정리해줘.&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;바이브 코딩은 AI에게 많이 맡기는 방식이지만, 방향은 사람이 잡아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;8. 테스트 기준 정하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 만든 코드는 그럴듯하게 보일 때가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 그럴듯한 코드와 제대로 동작하는 코드는 다르다. 그래서 만들기 전에 테스트 기준을 정해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트라고 해서 처음부터 복잡한 자동화 테스트를 말하는 것은 아니다. 최소한 사용자가 직접 확인할 시나리오는 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 구독 관리 앱이라면 이렇게 정할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&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;이 정도만 있어도 AI가 만든 결과물을 확인하기 쉬워진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러 상황도 미리 정해야 한다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&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;이 질문을 하지 않으면 AI가 임의로 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로는 이런 작은 예외 처리가 앱의 완성도를 좌우한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;9. 배포와 유지보수 기준 정하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 배포까지 생각할 필요가 없다고 느낄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 어디에서 실행할지는 미리 정하는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 개인용 웹앱이라면 다음 선택지가 있다.&lt;/p&gt;
&lt;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;GitHub Pages에 정적 페이지로 배포&lt;/li&gt;
&lt;li&gt;Vercel이나 Netlify에 배포&lt;/li&gt;
&lt;li&gt;서버와 데이터베이스까지 연결해 배포&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 선택지에 따라 구현 방식이 달라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 페이지로 배포할 앱이라면 서버 기능을 넣으면 안 된다. 반대로 사용자별 데이터 저장이 필요하다면 백엔드나 외부 데이터베이스가 필요할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유지보수 기준도 정해야 한다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;내가 직접 코드를 읽고 수정할 수 있어야 하는가?
AI에게 계속 맡길 것인가?
파일 구조를 단순하게 유지할 것인가?
기능 추가보다 안정성을 우선할 것인가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바이브 코딩으로 만든 프로젝트는 초반 속도가 빠르다. 하지만 구조가 복잡해진 뒤에는 사람이 이해하기 어려워질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 작은 프로젝트일수록 파일 구조와 기능 범위를 단순하게 유지하는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;바이브 코딩 시작 전 체크리스트&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 항목을 채운 뒤 AI 코딩 도구를 시작하면 결과물이 훨씬 안정적이다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;정해야 할 것&lt;/th&gt;
&lt;th&gt;예시&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;프로젝트 정의&lt;/td&gt;
&lt;td&gt;무엇을 만들 것인가&lt;/td&gt;
&lt;td&gt;블로그 제목 SEO 점검 도구&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;사용자&lt;/td&gt;
&lt;td&gt;누가 쓰는가&lt;/td&gt;
&lt;td&gt;티스토리 블로그 운영자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;사용 상황&lt;/td&gt;
&lt;td&gt;언제 쓰는가&lt;/td&gt;
&lt;td&gt;글 발행 전 제목과 설명을 점검할 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;핵심 기능&lt;/td&gt;
&lt;td&gt;반드시 필요한 기능&lt;/td&gt;
&lt;td&gt;제목 입력, 메타 설명 입력, 점검 결과 표시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;제외 기능&lt;/td&gt;
&lt;td&gt;지금 만들지 않을 기능&lt;/td&gt;
&lt;td&gt;로그인, 통계, 자동 발행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;화면 흐름&lt;/td&gt;
&lt;td&gt;어떤 순서로 쓰는가&lt;/td&gt;
&lt;td&gt;입력 &amp;rarr; 점검 &amp;rarr; 결과&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;데이터 구조&lt;/td&gt;
&lt;td&gt;어떤 정보를 저장하는가&lt;/td&gt;
&lt;td&gt;제목, 설명, 본문, 키워드&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;저장 방식&lt;/td&gt;
&lt;td&gt;어디에 저장하는가&lt;/td&gt;
&lt;td&gt;처음에는 localStorage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;권한&lt;/td&gt;
&lt;td&gt;로그인이나 관리자 기능이 필요한가&lt;/td&gt;
&lt;td&gt;개인용이면 로그인 제외&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;보안 기준&lt;/td&gt;
&lt;td&gt;민감한 값을 어떻게 관리할 것인가&lt;/td&gt;
&lt;td&gt;API 키는 .env로 분리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;테스트 기준&lt;/td&gt;
&lt;td&gt;무엇이 되면 성공인가&lt;/td&gt;
&lt;td&gt;입력값에 따라 결과가 표시됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;배포 방식&lt;/td&gt;
&lt;td&gt;어디에서 실행할 것인가&lt;/td&gt;
&lt;td&gt;로컬 실행 또는 Vercel 배포&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;핵심은 거창한 문서를 만드는 것이 아니다. AI가 마음대로 추측하지 않도록 최소한의 기준을 정해두는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;AI에게 처음 줄 프롬프트 예시&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 바이브 코딩을 시작할 때 사용할 수 있는 첫 프롬프트 예시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 API 키나 토큰이 필요한 프로젝트라면, 보안 조건을 프롬프트에 처음부터 넣어두는 것이 좋다. 나중에 고치려고 하면 이미 코드 곳곳에 민감한 값이 섞여 있을 수 있다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;나는 티스토리 블로그 운영자를 위한 간단한 SEO 점검 웹앱을 만들고 싶다.

목표:
- 글 제목, 메타 설명, 핵심 키워드를 입력하면 기본 점검 결과를 보여준다.
- 처음 버전에서는 로그인, 서버, 데이터베이스는 만들지 않는다.
- 데이터는 브라우저 localStorage에만 저장한다.

사용자:
- 티스토리 블로그 글을 직접 작성하는 개인 운영자

핵심 기능:
1. 제목 입력
2. 메타 설명 입력
3. 핵심 키워드 입력
4. 점검하기 버튼
5. 제목 길이, 메타 설명 길이, 키워드 포함 여부를 결과로 표시
6. 최근 점검 기록 5개 저장

제외할 기능:
- 로그인
- 서버 API
- 자동 글 생성
- 티스토리 자동 업로드
- 결제 기능

보안 조건:
- API 키, 비밀번호, 토큰 같은 민감한 값은 코드에 직접 넣지 않는다.
- 민감한 값이 필요해지는 경우 .env 환경 변수로 분리한다.
- .env 파일은 Git에 올라가지 않도록 .gitignore에 추가한다.

요청:
- 먼저 React 기준으로 화면과 기본 동작을 만들어줘.
- 디자인은 단순해도 된다.
- 한 파일에 너무 많은 코드가 몰리지 않게 컴포넌트를 나눠줘.
- 수정한 파일 목록과 실행 방법을 마지막에 정리해줘.&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;이렇게 요청하면 AI가 추측해야 할 부분이 줄어든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 &amp;ldquo;멋진 서비스처럼 만들어줘&amp;rdquo;라고 하는 것보다 훨씬 낫다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;주의할 점: AI가 만든 코드를 그대로 믿지 않기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바이브 코딩은 빠르지만, 검토가 필요 없는 방식은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI는 존재하지 않는 패키지를 추천할 수 있고, 오래된 문법을 섞을 수 있다. 보안상 위험한 코드를 자연스럽게 넣을 때도 있다. 에러가 나면 원인을 정확히 고치기보다 우회하는 코드를 만들기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 아래 항목은 반드시 확인해야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;설치하라는 패키지가 실제로 존재하는지&lt;/li&gt;
&lt;li&gt;비밀번호나 API 키가 코드에 직접 들어가지 않았는지&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.env&lt;/code&gt; 파일이 Git에 올라가도록 되어 있지 않은지&lt;/li&gt;
&lt;li&gt;파일 삭제, DB 초기화 같은 위험한 명령이 포함되지 않았는지&lt;/li&gt;
&lt;li&gt;로그인이나 인증 코드가 너무 단순하게 처리되지 않았는지&lt;/li&gt;
&lt;li&gt;에러를 숨기기만 하고 실제 원인을 해결하지 않은 것은 아닌지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 초보자라면 더더욱 작은 단위로 실행하고 확인해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 &amp;ldquo;완성했습니다&amp;rdquo;라고 말해도 실제로 완성된 것은 아니다. 실행해보고, 눌러보고, 이상한 값을 넣어봐야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;작은 프로젝트부터 시작하는 게 좋다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바이브 코딩을 실전에서 익히려면 처음부터 큰 서비스를 만들기보다 작고 명확한 도구가 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 이런 주제가 적당하다.&lt;/p&gt;
&lt;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;CSV 파일 정리 도구&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;공통점은 기능이 작고, 데이터 구조가 단순하고, 민감한 정보를 많이 다루지 않는다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 결제, 로그인, 관리자 페이지, 실시간 채팅, 복잡한 권한 기능이 들어가는 프로젝트는 피하는 편이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 코드를 만들어줄 수는 있지만, 문제가 생겼을 때 사용자가 이해하기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;마무리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바이브 코딩은 아이디어를 빠르게 결과물로 바꾸는 데 유용하다. 하지만 만들기 전에 아무것도 정하지 않으면 AI가 프로젝트의 방향까지 대신 정해버린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 중요한 것은 프롬프트를 화려하게 쓰는 것이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무엇을 만들지, 누구를 위한 것인지, 어디까지 만들지, 어떤 기준으로 확인할지를 먼저 정하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 작은 기능 하나만 제대로 동작하게 만드는 것이 좋다. 그다음 기능을 추가해도 늦지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바이브 코딩을 실전에서 쓰고 싶다면, 코드를 요청하기 전에 체크리스트부터 채워보는 편이 낫다. 그 10분이 나중에 몇 시간짜리 수정 작업을 줄여준다.&lt;/p&gt;</description>
      <category>AI/AI 코딩</category>
      <category>AI코딩</category>
      <category>AI코딩에이전트</category>
      <category>CURSOR</category>
      <category>개발도구</category>
      <category>콘텐츠전략</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/57</guid>
      <comments>https://notebase.tistory.com/entry/vibe-coding-practical-checklist-before-building#entry57comment</comments>
      <pubDate>Mon, 1 Jun 2026 09:00:42 +0900</pubDate>
    </item>
    <item>
      <title>FastAPI CORS 설정 방법: 프론트엔드 연동할 때 막히는 이유</title>
      <link>https://notebase.tistory.com/entry/fastapi-cors-frontend-integration</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;React, Vue, Next.js 같은 프론트엔드에서 FastAPI API 호출이 CORS 오류로 막히는 이유와 CORSMiddleware 설정 방법을 초보자 기준으로 정리합니다.&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;FastAPI CORS 설정은 프론트엔드에서 백엔드 API를 호출할 때 자주 막히는 지점이다. React, Vue, Next.js 개발 서버와 FastAPI 서버를 따로 실행하면 브라우저 콘솔에 CORS 오류가 뜨는 경우가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 보면 FastAPI 코드가 잘못된 것처럼 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제로는 API 로직 문제가 아니라 &lt;b&gt;브라우저 보안 정책 때문에 요청이 차단되는 상황&lt;/b&gt;인 경우가 많다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;CORS가 뭔가요?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CORS는 &lt;b&gt;Cross-Origin Resource Sharing&lt;/b&gt;의 줄임말이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한국어로 풀면 &amp;ldquo;교차 출처 리소스 공유&amp;rdquo; 정도로 번역할 수 있다. 말은 어렵지만, 핵심은 단순하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 출처에서 온 요청을 서버가 허용할지 정하는 방식&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 프론트엔드는 아래 주소에서 실행 중이라고 해보자.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://localhost:5173&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;FastAPI 서버는 아래 주소에서 실행 중이다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://localhost:8000&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;이 상태에서 프론트엔드가 FastAPI 서버로 요청을 보내면 브라우저는 먼저 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;http://localhost:5173&lt;/code&gt;에서 온 요청이 &lt;code&gt;http://localhost:8000&lt;/code&gt; 서버에 접근해도 되나요?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI 서버가 &amp;ldquo;허용한다&amp;rdquo;고 알려주면 응답을 프론트엔드 코드에서 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 서버가 허용 정보를 주지 않으면 브라우저가 응답을 막고 CORS 오류를 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점은 CORS가 &lt;b&gt;서버가 죽어서 나는 오류&lt;/b&gt;가 아니라는 것이다. 서버는 정상 응답을 했는데, 브라우저가 그 응답을 프론트엔드 코드에 넘기지 않는 경우도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 CORS 설정은 단순히 오류를 없애는 코드가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;어떤 프론트엔드 주소가 내 FastAPI 서버에 접근할 수 있는지 정하는 설정&lt;/b&gt;이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;CORS 오류는 왜 발생할까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹에서는 다음 세 가지가 모두 같아야 같은 출처, 즉 같은 Origin으로 본다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;프로토콜: http 또는 https
도메인: localhost, example.com 등
포트: 5173, 8000 등&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 두 주소를 비교해보자.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://localhost:5173
http://localhost:8000&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;code&gt;http&lt;/code&gt;로 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도메인도 &lt;code&gt;localhost&lt;/code&gt;로 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 포트가 다르다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;프론트엔드: http://localhost:5173
백엔드:     http://localhost:8000&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;그래서 브라우저는 이 둘을 서로 다른 Origin으로 본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드 개발 서버와 백엔드 API 서버를 분리해서 실행하면 CORS 오류를 자주 만나는 이유가 여기에 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;FastAPI에서 CORS 설정하는 기본 방법&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI에서는 &lt;code&gt;CORSMiddleware&lt;/code&gt;를 추가해서 CORS를 설정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI 공식 문서에서도 CORS 설정은 &lt;code&gt;CORSMiddleware&lt;/code&gt;를 임포트하고, 허용할 Origin 목록을 만든 뒤, FastAPI 애플리케이션에 미들웨어로 추가하는 방식으로 안내한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 코드는 아래와 같다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;# main.py

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

origins = [
    &quot;http://localhost:5173&quot;,
    &quot;http://127.0.0.1:5173&quot;,
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=[&quot;GET&quot;, &quot;POST&quot;, &quot;PUT&quot;, &quot;DELETE&quot;, &quot;OPTIONS&quot;],
    allow_headers=[&quot;Authorization&quot;, &quot;Content-Type&quot;],
)


@app.get(&quot;/&quot;)
def read_root():
    return {&quot;message&quot;: &quot;Hello FastAPI&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;code&gt;allow_origins&lt;/code&gt;다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;origins = [
    &quot;http://localhost:5173&quot;,
    &quot;http://127.0.0.1:5173&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vite 기반 React 프로젝트라면 보통 아래 주소를 많이 쓴다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://localhost:5173&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;Create React App이나 Next.js 개발 서버는 기본적으로 아래 주소를 많이 쓴다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://localhost:3000&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;각 옵션은 무슨 의미일까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CORS 설정 코드를 처음 보면 옵션이 많아서 헷갈릴 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=[&quot;GET&quot;, &quot;POST&quot;, &quot;PUT&quot;, &quot;DELETE&quot;, &quot;OPTIONS&quot;],
    allow_headers=[&quot;Authorization&quot;, &quot;Content-Type&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;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;옵션&lt;/th&gt;
&lt;th&gt;의미&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;allow_origins&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;요청을 허용할 프론트엔드 주소&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;allow_credentials&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;쿠키, 인증 헤더 등 인증 정보를 포함할 수 있는지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;allow_methods&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;허용할 HTTP 메서드&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;allow_headers&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;허용할 요청 헤더&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 중에는 아래처럼 넓게 허용하는 예시도 자주 보인다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;allow_methods=[&quot;*&quot;]
allow_headers=[&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;code&gt;allow_credentials=True&lt;/code&gt;를 함께 사용할 때는 주의해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI 공식 문서 기준으로 &lt;code&gt;allow_credentials=True&lt;/code&gt;인 경우 &lt;code&gt;allow_origins&lt;/code&gt;, &lt;code&gt;allow_methods&lt;/code&gt;, &lt;code&gt;allow_headers&lt;/code&gt;는 &lt;code&gt;[&quot;*&quot;]&lt;/code&gt;로 설정할 수 없다. 모두 명시적으로 지정해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 쿠키나 인증 정보를 포함하는 요청을 다룬다면 아래처럼 실제 값을 적어주는 편이 좋다.&lt;/p&gt;
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;app.add_middleware(
    CORSMiddleware,
    allow_origins=[
        &quot;http://localhost:5173&quot;,
        &quot;https://example.com&quot;,
    ],
    allow_credentials=True,
    allow_methods=[&quot;GET&quot;, &quot;POST&quot;, &quot;PUT&quot;, &quot;DELETE&quot;, &quot;OPTIONS&quot;],
    allow_headers=[&quot;Authorization&quot;, &quot;Content-Type&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;입문 단계에서는 이 부분을 놓치기 쉽다. 단순 조회 API만 테스트할 때는 문제가 없어 보이다가, 로그인이나 인증 요청을 붙이는 순간 CORS 오류가 다시 나타날 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;프론트엔드 요청 예시&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드에서는 보통 &lt;code&gt;fetch&lt;/code&gt;나 &lt;code&gt;axios&lt;/code&gt;로 FastAPI API를 호출한다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;fetch(&quot;http://localhost:8000/&quot;)
  .then((response) =&amp;gt; response.json())
  .then((data) =&amp;gt; {
    console.log(data);
  });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI에 CORS 설정이 없거나, &lt;code&gt;allow_origins&lt;/code&gt;에 현재 프론트엔드 주소가 빠져 있으면 브라우저 콘솔에 대략 이런 오류가 뜰 수 있다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;Access to fetch at 'http://localhost:8000/'
from origin 'http://localhost:5173'
has been blocked by CORS policy&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 메시지에서 봐야 할 부분은 두 가지다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;fetch at 'http://localhost:8000/'
origin 'http://localhost:5173'&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;code&gt;http://localhost:5173&lt;/code&gt;에서 &lt;code&gt;http://localhost:8000&lt;/code&gt;으로 요청했는데 허용되지 않았다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 FastAPI 설정의 &lt;code&gt;allow_origins&lt;/code&gt;에 &lt;code&gt;http://localhost:5173&lt;/code&gt;을 추가해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;code&gt;localhost&lt;/code&gt;와 &lt;code&gt;127.0.0.1&lt;/code&gt;은 다르게 취급될 수 있다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입문자가 자주 놓치는 부분이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 두 주소는 비슷해 보인다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://localhost:5173
http://127.0.0.1:5173&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;하지만 Origin 기준에서는 문자열이 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드를 &lt;code&gt;http://127.0.0.1:5173&lt;/code&gt;으로 열어놓고, FastAPI CORS 설정에는 &lt;code&gt;http://localhost:5173&lt;/code&gt;만 넣으면 CORS 오류가 날 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 개발 환경에서는 둘 다 넣어두는 편이 안전하다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;origins = [
    &quot;http://localhost:5173&quot;,
    &quot;http://127.0.0.1:5173&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;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;임시로 모든 Origin을 허용해도 될까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 중에는 아래처럼 모든 Origin을 허용하는 예시를 볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;app.add_middleware(
    CORSMiddleware,
    allow_origins=[&quot;*&quot;],
    allow_methods=[&quot;*&quot;],
    allow_headers=[&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 설정을 그대로 운영 환경에 가져가는 것은 조심해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 로그인, 쿠키, 인증 토큰처럼 사용자 정보가 오가는 API라면 더 신중해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 &lt;code&gt;allow_credentials=True&lt;/code&gt;를 사용한다면 아래처럼 와일드카드와 섞어서 쓰지 않는 편이 좋다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# 피해야 할 예시
app.add_middleware(
    CORSMiddleware,
    allow_origins=[&quot;*&quot;],
    allow_credentials=True,
    allow_methods=[&quot;*&quot;],
    allow_headers=[&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;FastAPI 공식 문서에서도 credentials를 허용하는 경우에는 &lt;code&gt;allow_origins&lt;/code&gt;, &lt;code&gt;allow_methods&lt;/code&gt;, &lt;code&gt;allow_headers&lt;/code&gt;를 모두 명시적으로 지정해야 한다고 안내한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영 환경에서는 보통 실제 서비스 도메인만 허용한다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;origins = [
    &quot;https://example.com&quot;,
    &quot;https://www.example.com&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;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;쿠키나 인증 정보를 보낼 때는 더 주의해야 한다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드에서 쿠키 기반 로그인이나 인증 헤더를 사용한다면 &lt;code&gt;allow_credentials=True&lt;/code&gt;가 필요할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 axios에서 쿠키를 포함해 요청하려면 프론트엔드 코드에도 설정이 들어간다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;axios.get(&quot;http://localhost:8000/me&quot;, {
  withCredentials: true,
});&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;FastAPI 쪽에서는 아래처럼 설정한다.&lt;/p&gt;
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;app.add_middleware(
    CORSMiddleware,
    allow_origins=[
        &quot;http://localhost:5173&quot;,
    ],
    allow_credentials=True,
    allow_methods=[&quot;GET&quot;, &quot;POST&quot;, &quot;PUT&quot;, &quot;DELETE&quot;, &quot;OPTIONS&quot;],
    allow_headers=[&quot;Authorization&quot;, &quot;Content-Type&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증 정보가 오가는 요청이라면 어떤 프론트엔드 도메인에서 접근할 수 있는지 명확히 제한해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영 환경에서 &lt;code&gt;allow_origins=[&quot;*&quot;]&lt;/code&gt;처럼 모든 출처를 열어두는 방식은 피하는 편이 좋다. 특히 &lt;code&gt;allow_credentials=True&lt;/code&gt;와 함께 쓸 때는 &lt;code&gt;allow_methods&lt;/code&gt;, &lt;code&gt;allow_headers&lt;/code&gt;까지 실제 필요한 값으로 명시하는 것이 공식 문서 기준에 맞다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Preflight 요청 때문에 막히는 경우도 있다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CORS 오류를 보다 보면 실제 요청 전에 &lt;code&gt;OPTIONS&lt;/code&gt; 요청이 먼저 나가는 경우가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 &lt;b&gt;Preflight 요청&lt;/b&gt;이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저가 본 요청을 보내기 전에 서버에 먼저 확인하는 과정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 Origin에서 이런 메서드와 헤더로 요청해도 되나요?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &lt;code&gt;Authorization&lt;/code&gt; 헤더를 붙이거나, &lt;code&gt;Content-Type: application/json&lt;/code&gt;으로 &lt;code&gt;POST&lt;/code&gt; 요청을 보내는 경우 Preflight 요청이 발생할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MDN 문서에서도 Preflight 요청은 실제 요청 전에 브라우저가 &lt;code&gt;OPTIONS&lt;/code&gt; 메서드로 서버에 확인하는 요청이라고 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI에서는 &lt;code&gt;CORSMiddleware&lt;/code&gt;를 올바르게 추가하면 이런 Preflight 요청도 처리된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 보통은 개발자가 &lt;code&gt;OPTIONS&lt;/code&gt; 라우터를 직접 만들 필요가 없다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;CORS 설정했는데도 안 될 때 확인할 것&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CORS 설정을 했는데도 계속 오류가 난다면 아래 순서로 확인해보면 좋다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 프론트엔드 주소가 정확한가?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저 주소창을 확인한다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://localhost:5173
http://127.0.0.1:5173
http://localhost:3000&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;code&gt;allow_origins&lt;/code&gt;에 정확히 들어가 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;끝에 &lt;code&gt;/&lt;/code&gt;를 붙여서 아래처럼 쓰면 의도와 다르게 동작할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;vala&quot;&gt;&lt;code&gt;# 권장하지 않음
&quot;http://localhost:5173/&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;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;http://localhost:5173&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. FastAPI 서버를 재시작했는가?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CORS 설정을 바꿨는데 서버가 이전 코드로 계속 실행 중일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 서버를 재시작하거나, &lt;code&gt;--reload&lt;/code&gt; 옵션을 사용 중인지 확인한다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;fastapi dev main.py&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;또는 uvicorn을 직접 실행한다면 아래처럼 실행할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;uvicorn main:app --reload&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 요청 URL이 맞는가?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드에서 요청하는 주소가 실제 FastAPI 서버 주소와 다를 수 있다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;fetch(&quot;http://localhost:8000/api/items&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;FastAPI 서버가 &lt;code&gt;8000&lt;/code&gt;번 포트에서 실행 중인지, API 경로가 &lt;code&gt;/api/items&lt;/code&gt;가 맞는지 확인해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CORS 오류처럼 보이지만 실제로는 경로 오류, 프록시 설정 오류, 서버 실행 오류가 섞여 있는 경우도 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. 프론트엔드 프록시 설정과 충돌하지 않는가?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vite나 Next.js에서 프록시를 설정해두면 요청 흐름이 달라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 프론트엔드에서 아래처럼 요청한다고 해보자.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;fetch(&quot;/api/items&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;code&gt;http://localhost:8000&lt;/code&gt;으로 요청하는 것이 아니라, 프론트엔드 개발 서버의 프록시 설정을 거칠 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프록시를 사용할지, FastAPI에 직접 요청할지 먼저 정해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘을 섞어두면 오류 원인을 찾기 어렵다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5. 커스텀 헤더를 보내고 있지 않은가?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드에서 직접 만든 헤더를 보내는 경우도 있다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;fetch(&quot;http://localhost:8000/items&quot;, {
  headers: {
    &quot;X-Client-Version&quot;: &quot;1.0.0&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;이런 헤더를 사용한다면 FastAPI의 &lt;code&gt;allow_headers&lt;/code&gt;에도 해당 헤더 이름을 추가해야 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=[&quot;GET&quot;, &quot;POST&quot;, &quot;PUT&quot;, &quot;DELETE&quot;, &quot;OPTIONS&quot;],
    allow_headers=[
        &quot;Authorization&quot;,
        &quot;Content-Type&quot;,
        &quot;X-Client-Version&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;code&gt;Authorization&lt;/code&gt;, &lt;code&gt;Content-Type&lt;/code&gt; 외에 프로젝트에서 직접 사용하는 헤더가 있다면 이 목록에 빠지지 않았는지 확인해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 사내 프로젝트나 토이 프로젝트에서 &lt;code&gt;X-API-Key&lt;/code&gt;, &lt;code&gt;X-Client-Id&lt;/code&gt; 같은 헤더를 직접 붙여 쓰는 경우가 있다. 이때도 같은 방식으로 &lt;code&gt;allow_headers&lt;/code&gt;에 추가하면 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;개발 환경과 운영 환경 설정을 나누는 예시&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 프로젝트에서는 환경 변수로 허용 Origin을 관리하는 방식이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 예시는 아래와 같다.&lt;/p&gt;
&lt;pre class=&quot;nix&quot;&gt;&lt;code&gt;# main.py

import os

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

ENV = os.getenv(&quot;ENV&quot;, &quot;development&quot;)

if ENV == &quot;production&quot;:
    origins = [
        &quot;https://example.com&quot;,
        &quot;https://www.example.com&quot;,
    ]
else:
    origins = [
        &quot;http://localhost:5173&quot;,
        &quot;http://127.0.0.1:5173&quot;,
        &quot;http://localhost:3000&quot;,
        &quot;http://127.0.0.1:3000&quot;,
    ]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=[&quot;GET&quot;, &quot;POST&quot;, &quot;PUT&quot;, &quot;DELETE&quot;, &quot;OPTIONS&quot;],
    allow_headers=[&quot;Authorization&quot;, &quot;Content-Type&quot;],
)


@app.get(&quot;/health&quot;)
def health_check():
    return {&quot;status&quot;: &quot;ok&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 프로젝트에서 커스텀 헤더를 사용한다면 이 예시의 &lt;code&gt;allow_headers&lt;/code&gt;에도 해당 헤더를 추가하면 된다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;allow_headers=[
    &quot;Authorization&quot;,
    &quot;Content-Type&quot;,
    &quot;X-Client-Version&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;code&gt;.env&lt;/code&gt; 파일이나 배포 플랫폼의 환경 변수 설정을 사용할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;FastAPI CORS 설정 전체 예시&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입문자라면 일단 아래 코드로 시작해도 충분하다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;# main.py

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

origins = [
    &quot;http://localhost:5173&quot;,
    &quot;http://127.0.0.1:5173&quot;,
    &quot;http://localhost:3000&quot;,
    &quot;http://127.0.0.1:3000&quot;,
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=[&quot;GET&quot;, &quot;POST&quot;, &quot;PUT&quot;, &quot;DELETE&quot;, &quot;OPTIONS&quot;],
    allow_headers=[
        &quot;Authorization&quot;,
        &quot;Content-Type&quot;,
        # 커스텀 헤더를 사용한다면 여기에 추가
        # &quot;X-Client-Version&quot;,
    ],
)


@app.get(&quot;/&quot;)
def read_root():
    return {
        &quot;message&quot;: &quot;FastAPI CORS 설정 완료&quot;
    }


@app.get(&quot;/items&quot;)
def read_items():
    return [
        {&quot;id&quot;: 1, &quot;name&quot;: &quot;Apple&quot;},
        {&quot;id&quot;: 2, &quot;name&quot;: &quot;Banana&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;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;fetch(&quot;http://localhost:8000/items&quot;)
  .then((response) =&amp;gt; response.json())
  .then((data) =&amp;gt; console.log(data));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저 콘솔에 데이터가 정상 출력되면 기본 연동은 성공이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;만약 인증이 없는 단순 API라면?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인, 쿠키, 인증 헤더가 전혀 없는 단순 테스트 API라면 개발 중에는 아래처럼 간단히 열어둘 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;app.add_middleware(
    CORSMiddleware,
    allow_origins=[&quot;*&quot;],
    allow_methods=[&quot;*&quot;],
    allow_headers=[&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;다만 이 설정은 &amp;ldquo;테스트 편의용&amp;rdquo;에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나중에 로그인 기능이나 인증 헤더를 붙일 예정이라면 처음부터 명시적인 Origin 방식으로 작성하는 편이 덜 헷갈린다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;CORS는 에러를 없애는 설정이 아니라 허용 범위를 정하는 설정이다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CORS를 단순히 &amp;ldquo;오류 해결용 코드&amp;rdquo;로만 보면 나중에 운영 환경에서 설정을 과하게 열어둘 수 있다.&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 프론트엔드 주소가 이 API 서버에 접근할 수 있는가?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 중에는 &lt;code&gt;localhost&lt;/code&gt;와 &lt;code&gt;127.0.0.1&lt;/code&gt;을 넉넉히 허용해도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 배포할 때는 실제 서비스 도메인만 남기는 편이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI에서 프론트엔드 연동이 막힌다면 먼저 브라우저 콘솔의 CORS 메시지를 보고, 요청을 보낸 Origin과 &lt;code&gt;allow_origins&lt;/code&gt; 값을 비교해보면 된다. 대부분의 로컬 개발 CORS 오류는 여기서 원인이 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 인증 요청이 포함된다면 &lt;code&gt;allow_credentials=True&lt;/code&gt;와 와일드카드 설정을 함께 쓰고 있지 않은지도 같이 확인해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 직접 만든 헤더를 프론트엔드에서 보내고 있다면 &lt;code&gt;allow_headers&lt;/code&gt;에 해당 헤더 이름이 포함되어 있는지도 확인하자.&lt;/p&gt;</description>
      <category>개발/FastAPI</category>
      <category>fastapi</category>
      <category>개발도구</category>
      <category>코딩입문</category>
      <category>파이썬</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/56</guid>
      <comments>https://notebase.tistory.com/entry/fastapi-cors-frontend-integration#entry56comment</comments>
      <pubDate>Fri, 29 May 2026 16:17:43 +0900</pubDate>
    </item>
    <item>
      <title>FastAPI JWT 로그인 구현 기초: 토큰 인증 흐름 이해하기</title>
      <link>https://notebase.tistory.com/entry/fastapi-jwt-login-token-auth-flow</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI에서 JWT 로그인 인증이 어떻게 동작하는지 로그인 요청, 토큰 발급, Bearer 헤더 인증, 보호된 API 접근 흐름을 중심으로 정리합니다.&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;FastAPI JWT 로그인은 사용자가 로그인하면 서버가 토큰을 발급하고, 이후 요청에서 그 토큰을 확인해 사용자를 인증하는 방식이다. 핵심은 &amp;ldquo;로그인 상태를 서버가 계속 들고 있지 않는다&amp;rdquo;는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 웹 로그인에서는 서버 세션을 떠올리기 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 로그인하면 서버가 세션을 만들고, 브라우저는 쿠키를 들고 다닌다. 요청이 올 때마다 서버는 쿠키에 연결된 세션 정보를 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JWT 방식은 조금 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버는 로그인 성공 시 서명된 토큰을 만들어 클라이언트에 전달한다. 클라이언트는 이후 API 요청마다 이 토큰을 함께 보낸다. 서버는 토큰의 서명이 유효한지, 만료되지 않았는지 확인한 뒤 요청을 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점은 JWT가 &amp;ldquo;암호화된 비밀 저장소&amp;rdquo;가 아니라는 것이다. JWT 안의 내용은 누구나 디코딩해서 볼 수 있다. 그래서 비밀번호, 주민등록번호, 결제 정보 같은 민감한 값은 넣으면 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통은 사용자를 식별할 수 있는 값 정도만 넣는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 &lt;code&gt;user_id&lt;/code&gt;, &lt;code&gt;username&lt;/code&gt;, &lt;code&gt;sub&lt;/code&gt; 같은 값이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;FastAPI JWT 로그인 흐름을 먼저 이해하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드로 들어가기 전에 흐름부터 잡는 게 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JWT 로그인은 보통 이렇게 움직인다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사용자가 아이디와 비밀번호를 보낸다.&lt;/li&gt;
&lt;li&gt;서버가 사용자 정보를 확인한다.&lt;/li&gt;
&lt;li&gt;비밀번호가 맞으면 Access Token을 만든다.&lt;/li&gt;
&lt;li&gt;클라이언트는 토큰을 저장한다.&lt;/li&gt;
&lt;li&gt;이후 요청마다 &lt;code&gt;Authorization&lt;/code&gt; 헤더에 토큰을 담아 보낸다.&lt;/li&gt;
&lt;li&gt;서버는 토큰을 검증하고 사용자를 식별한다.&lt;/li&gt;
&lt;li&gt;토큰이 잘못됐거나 만료되면 &lt;code&gt;401 Unauthorized&lt;/code&gt;를 반환한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청 헤더는 보통 이런 형태가 된다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;Authorization: Bearer 발급받은_JWT_토큰&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;code&gt;Bearer&lt;/code&gt;는 &amp;ldquo;이 토큰을 가진 사용자를 인증된 사용자로 봐 달라&amp;rdquo;는 의미에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI에서는 이 흐름을 처리할 때 &lt;code&gt;OAuth2PasswordBearer&lt;/code&gt;를 자주 사용한다. 이름은 조금 길지만, 역할은 단순하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청 헤더에서 Bearer 토큰을 꺼내오는 도구라고 보면 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;프로젝트 준비&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 필요한 패키지를 설치한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;pip install fastapi uvicorn pyjwt passlib[bcrypt] python-multipart&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;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;패키지&lt;/th&gt;
&lt;th&gt;역할&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fastapi&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;API 서버 구현&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;uvicorn&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;FastAPI 실행 서버&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pyjwt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;JWT 생성 및 검증&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;passlib[bcrypt]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;비밀번호 해싱 및 검증&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;python-multipart&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;로그인 폼 데이터 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI 공식 문서의 JWT 예제에서는 다른 JWT 라이브러리를 사용하는 경우도 있다. 이 글에서는 &lt;code&gt;pyjwt&lt;/code&gt;를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;pyjwt&lt;/code&gt;는 JWT 생성과 검증에 집중한 라이브러리라 예제 코드 흐름이 단순하다. FastAPI에서 JWT 인증 구조를 익히는 목적이라면 &lt;code&gt;jwt.encode()&lt;/code&gt;, &lt;code&gt;jwt.decode()&lt;/code&gt;의 역할이 바로 보여서 이해하기 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 한 가지 주의할 점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패키지는 &lt;code&gt;pyjwt&lt;/code&gt;로 설치하지만, 코드에서 불러올 때는 &lt;code&gt;import jwt&lt;/code&gt;로 작성한다. &lt;code&gt;import pyjwt&lt;/code&gt;가 아니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import jwt&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예외 클래스도 같은 방식으로 가져온다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from jwt import PyJWTError&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;code&gt;python-multipart&lt;/code&gt;는 &lt;code&gt;OAuth2PasswordRequestForm&lt;/code&gt;을 사용할 때 필요하다. 설치하지 않으면 로그인 요청을 받을 때 오류가 날 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;사용자 데이터와 비밀번호 검증 구조&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 서비스라면 사용자 정보는 데이터베이스에 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 흐름 이해를 위해 메모리 딕셔너리를 사용한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from passlib.context import CryptContext

pwd_context = CryptContext(schemes=[&quot;bcrypt&quot;], deprecated=&quot;auto&quot;)

fake_users_db = {
    &quot;kim&quot;: {
        &quot;username&quot;: &quot;kim&quot;,
        &quot;hashed_password&quot;: pwd_context.hash(&quot;1234&quot;),
        &quot;disabled&quot;: False,
    },
    &quot;lee&quot;: {
        &quot;username&quot;: &quot;lee&quot;,
        &quot;hashed_password&quot;: pwd_context.hash(&quot;1234&quot;),
        &quot;disabled&quot;: True,
    },
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비밀번호는 원문 그대로 저장하면 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 사용자의 비밀번호가 &lt;code&gt;1234&lt;/code&gt;라고 해서 DB에 그대로 &lt;code&gt;1234&lt;/code&gt;를 넣는 방식은 피해야 한다. DB가 유출되면 사용자의 비밀번호가 바로 노출되기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 보통은 해시된 값을 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검증할 때도 원문끼리 비교하지 않는다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;def verify_password(plain_password: str, hashed_password: str) -&amp;gt; bool:
    return pwd_context.verify(plain_password, hashed_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;사용자 조회 함수도 따로 만든다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;def get_user(username: str):
    user = fake_users_db.get(username)
    if user:
        return user
    return None&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 로그인 시 사용할 인증 함수를 작성한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;def authenticate_user(username: str, password: str):
    user = get_user(username)

    if not user:
        return None

    if not verify_password(password, user[&quot;hashed_password&quot;]):
        return None

    return user&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;code&gt;None&lt;/code&gt;을 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 서비스에서는 &amp;ldquo;아이디가 없습니다&amp;rdquo;, &amp;ldquo;비밀번호가 틀렸습니다&amp;rdquo;를 너무 구체적으로 나누지 않는 경우도 있다. 공격자가 존재하는 계정을 추측하는 데 악용할 수 있기 때문이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;JWT Access Token 만들기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JWT를 만들 때는 크게 네 가지가 필요하다.&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;예시 코드는 다음과 같다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from datetime import datetime, timedelta, timezone
import jwt

SECRET_KEY = &quot;change-this-secret-key&quot;
ALGORITHM = &quot;HS256&quot;
ACCESS_TOKEN_EXPIRE_MINUTES = 30

def create_access_token(data: dict, expires_delta: timedelta | None = None):
    to_encode = data.copy()

    expire = datetime.now(timezone.utc) + (
        expires_delta or timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    )

    to_encode.update({&quot;exp&quot;: expire})

    encoded_jwt = jwt.encode(
        to_encode,
        SECRET_KEY,
        algorithm=ALGORITHM,
    )

    return encoded_jwt&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;code&gt;SECRET_KEY&lt;/code&gt;는 토큰 서명에 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 값이 노출되면 공격자가 유효한 토큰을 직접 만들 수 있다. 예제에서는 문자열로 적었지만, 실제 서비스에서는 환경 변수나 별도 시크릿 관리 도구로 관리하는 게 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;exp&lt;/code&gt;는 토큰 만료 시간이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JWT는 한 번 발급되면 서버가 별도 저장소에서 관리하지 않는 경우가 많다. 그래서 만료 시간이 없으면 탈취된 토큰이 계속 사용될 수 있다. Access Token은 보통 짧게 가져간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 30분으로 설정했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;로그인 API 구현하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 로그인 API를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI에서는 &lt;code&gt;OAuth2PasswordRequestForm&lt;/code&gt;을 사용하면 &lt;code&gt;username&lt;/code&gt;, &lt;code&gt;password&lt;/code&gt; 형식의 로그인 폼 데이터를 받을 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm
from datetime import timedelta

app = FastAPI()

@app.post(&quot;/token&quot;)
def login(form_data: OAuth2PasswordRequestForm = Depends()):
    user = authenticate_user(form_data.username, form_data.password)

    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail=&quot;아이디 또는 비밀번호가 올바르지 않습니다.&quot;,
            headers={&quot;WWW-Authenticate&quot;: &quot;Bearer&quot;},
        )

    access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)

    access_token = create_access_token(
        data={&quot;sub&quot;: user[&quot;username&quot;]},
        expires_delta=access_token_expires,
    )

    return {
        &quot;access_token&quot;: access_token,
        &quot;token_type&quot;: &quot;bearer&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;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;access_token&quot;: &quot;JWT_TOKEN_VALUE&quot;,
  &quot;token_type&quot;: &quot;bearer&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;code&gt;sub&lt;/code&gt;는 subject의 줄임말이다. 토큰이 누구를 가리키는지 담는 용도로 자주 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예시에서는 &lt;code&gt;username&lt;/code&gt;을 넣었다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;보호된 API에서 토큰 검증하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인 API가 토큰을 발급했다면, 이제 토큰이 있어야 접근할 수 있는 API를 만들어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 &lt;code&gt;OAuth2PasswordBearer&lt;/code&gt;를 설정한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from fastapi.security import OAuth2PasswordBearer

oauth2_scheme = OAuth2PasswordBearer(tokenUrl=&quot;token&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;code&gt;tokenUrl=&quot;token&quot;&lt;/code&gt;은 토큰을 발급받는 경로를 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에서 만든 로그인 API가 &lt;code&gt;/token&lt;/code&gt;이므로 이렇게 맞춘다. 이 설정은 Swagger UI의 Authorize 버튼과도 연결된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 현재 사용자를 가져오는 함수를 만든다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from jwt import PyJWTError

def get_current_user(token: str = Depends(oauth2_scheme)):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail=&quot;인증 정보를 확인할 수 없습니다.&quot;,
        headers={&quot;WWW-Authenticate&quot;: &quot;Bearer&quot;},
    )

    try:
        payload = jwt.decode(
            token,
            SECRET_KEY,
            algorithms=[ALGORITHM],
        )

        username: str | None = payload.get(&quot;sub&quot;)

        if username is None:
            raise credentials_exception

    except PyJWTError:
        raise credentials_exception

    user = get_user(username)

    if user is None:
        raise credentials_exception

    if user.get(&quot;disabled&quot;):
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail=&quot;비활성화된 계정입니다.&quot;,
        )

    return user&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;요청 헤더에서 Bearer 토큰을 꺼낸다.&lt;/li&gt;
&lt;li&gt;JWT 서명을 검증한다.&lt;/li&gt;
&lt;li&gt;토큰이 만료됐는지 확인한다.&lt;/li&gt;
&lt;li&gt;토큰 안의 &lt;code&gt;sub&lt;/code&gt; 값을 꺼낸다.&lt;/li&gt;
&lt;li&gt;해당 사용자가 실제로 존재하는지 확인한다.&lt;/li&gt;
&lt;li&gt;계정이 비활성화된 상태인지 확인한다.&lt;/li&gt;
&lt;li&gt;문제가 없으면 사용자 정보를 반환한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;disabled&lt;/code&gt; 확인이 빠지면, 정지되었거나 비활성화된 계정도 토큰만 유효하면 API에 접근할 수 있다. 그래서 사용자 조회 후 계정 상태를 한 번 더 확인하는 편이 안전하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 보호된 API에 적용한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;@app.get(&quot;/users/me&quot;)
def read_users_me(current_user: dict = Depends(get_current_user)):
    return {
        &quot;username&quot;: current_user[&quot;username&quot;],
        &quot;disabled&quot;: current_user[&quot;disabled&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;이 API는 토큰 없이 접근하면 실패한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 로그인해서 받은 토큰을 &lt;code&gt;Authorization: Bearer ...&lt;/code&gt; 헤더에 넣으면 정상 응답을 받을 수 있다. 다만 &lt;code&gt;disabled&lt;/code&gt; 값이 &lt;code&gt;True&lt;/code&gt;인 계정은 토큰이 유효해도 접근이 차단된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;전체 코드 예시&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드는 흐름 이해용으로 한 파일에 정리한 예시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 서비스에서는 사용자 모델, 인증 로직, 설정값, 라우터를 파일별로 나누는 편이 좋다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;# main.py

from datetime import datetime, timedelta, timezone

import jwt
from jwt import PyJWTError
from passlib.context import CryptContext

from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm


# ==================================================
# 1. 기본 설정
# ==================================================

SECRET_KEY = &quot;change-this-secret-key&quot;
ALGORITHM = &quot;HS256&quot;
ACCESS_TOKEN_EXPIRE_MINUTES = 30

app = FastAPI()

pwd_context = CryptContext(schemes=[&quot;bcrypt&quot;], deprecated=&quot;auto&quot;)
oauth2_scheme = OAuth2PasswordBearer(tokenUrl=&quot;token&quot;)


# ==================================================
# 2. 예시 사용자 데이터
# ==================================================

fake_users_db = {
    &quot;kim&quot;: {
        &quot;username&quot;: &quot;kim&quot;,
        &quot;hashed_password&quot;: pwd_context.hash(&quot;1234&quot;),
        &quot;disabled&quot;: False,
    },
    &quot;lee&quot;: {
        &quot;username&quot;: &quot;lee&quot;,
        &quot;hashed_password&quot;: pwd_context.hash(&quot;1234&quot;),
        &quot;disabled&quot;: True,
    },
}


# ==================================================
# 3. 비밀번호 검증과 사용자 인증
# ==================================================

def verify_password(plain_password: str, hashed_password: str) -&amp;gt; bool:
    return pwd_context.verify(plain_password, hashed_password)


def get_user(username: str):
    user = fake_users_db.get(username)

    if user:
        return user

    return None


def authenticate_user(username: str, password: str):
    user = get_user(username)

    if not user:
        return None

    if not verify_password(password, user[&quot;hashed_password&quot;]):
        return None

    return user


# ==================================================
# 4. JWT Access Token 생성
# ==================================================

def create_access_token(data: dict, expires_delta: timedelta | None = None):
    to_encode = data.copy()

    expire = datetime.now(timezone.utc) + (
        expires_delta or timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    )

    to_encode.update({&quot;exp&quot;: expire})

    encoded_jwt = jwt.encode(
        to_encode,
        SECRET_KEY,
        algorithm=ALGORITHM,
    )

    return encoded_jwt


# ==================================================
# 5. Bearer 토큰 검증과 현재 사용자 조회
# ==================================================

def get_current_user(token: str = Depends(oauth2_scheme)):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail=&quot;인증 정보를 확인할 수 없습니다.&quot;,
        headers={&quot;WWW-Authenticate&quot;: &quot;Bearer&quot;},
    )

    try:
        payload = jwt.decode(
            token,
            SECRET_KEY,
            algorithms=[ALGORITHM],
        )

        username: str | None = payload.get(&quot;sub&quot;)

        if username is None:
            raise credentials_exception

    except PyJWTError:
        raise credentials_exception

    user = get_user(username)

    if user is None:
        raise credentials_exception

    if user.get(&quot;disabled&quot;):
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail=&quot;비활성화된 계정입니다.&quot;,
        )

    return user


# ==================================================
# 6. 로그인 API
# ==================================================

@app.post(&quot;/token&quot;)
def login(form_data: OAuth2PasswordRequestForm = Depends()):
    user = authenticate_user(form_data.username, form_data.password)

    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail=&quot;아이디 또는 비밀번호가 올바르지 않습니다.&quot;,
            headers={&quot;WWW-Authenticate&quot;: &quot;Bearer&quot;},
        )

    access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)

    access_token = create_access_token(
        data={&quot;sub&quot;: user[&quot;username&quot;]},
        expires_delta=access_token_expires,
    )

    return {
        &quot;access_token&quot;: access_token,
        &quot;token_type&quot;: &quot;bearer&quot;,
    }


# ==================================================
# 7. 보호된 API
# ==================================================

@app.get(&quot;/users/me&quot;)
def read_users_me(current_user: dict = Depends(get_current_user)):
    return {
        &quot;username&quot;: current_user[&quot;username&quot;],
        &quot;disabled&quot;: current_user[&quot;disabled&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;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;uvicorn main:app --reload&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 후 아래 주소로 접속한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;http://127.0.0.1:8000/docs&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Swagger UI에서 테스트하는 방법&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI의 장점 중 하나는 인증 API도 Swagger UI에서 바로 테스트할 수 있다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순서는 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;/docs&lt;/code&gt;에 접속한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/token&lt;/code&gt; API를 연다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Try it out&lt;/code&gt;을 클릭한다.&lt;/li&gt;
&lt;li&gt;username에 &lt;code&gt;kim&lt;/code&gt;, password에 &lt;code&gt;1234&lt;/code&gt;를 입력한다.&lt;/li&gt;
&lt;li&gt;응답으로 &lt;code&gt;access_token&lt;/code&gt;을 받는다.&lt;/li&gt;
&lt;li&gt;우측 상단 &lt;code&gt;Authorize&lt;/code&gt; 버튼을 누른다.&lt;/li&gt;
&lt;li&gt;토큰 값을 입력한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/users/me&lt;/code&gt; API를 호출한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 헷갈리기 쉬운 부분이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swagger UI의 Authorize 창에는 보통 토큰 값만 넣으면 된다. 직접 &lt;code&gt;Bearer&lt;/code&gt;까지 붙이지 않아도 되는 경우가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 Postman, curl, 프론트엔드 코드에서 직접 요청을 보낼 때는 아래처럼 헤더를 구성해야 한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;Authorization: Bearer 발급받은_토큰&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;curl로 요청하면 이런 형태다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl -X GET &quot;http://127.0.0.1:8000/users/me&quot; \
  -H &quot;Authorization: Bearer 발급받은_토큰&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시 코드에서는 &lt;code&gt;lee&lt;/code&gt; 계정의 &lt;code&gt;disabled&lt;/code&gt; 값이 &lt;code&gt;True&lt;/code&gt;다. 이 계정으로 로그인하면 토큰은 발급되지만, &lt;code&gt;/users/me&lt;/code&gt; 요청에서는 &lt;code&gt;403 Forbidden&lt;/code&gt; 응답을 받는다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;username: lee
password: 1234&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;ldquo;로그인 성공&amp;rdquo;과 &amp;ldquo;API 접근 허용&amp;rdquo;이 항상 같은 의미는 아니라는 점을 이해하기 쉽다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;JWT 로그인에서 자주 헷갈리는 부분&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;JWT는 로그인 상태를 저장하는 방식이 아니다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JWT는 서버 세션과 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버가 &amp;ldquo;이 사용자는 로그인 중이다&amp;rdquo;라는 상태를 매번 저장해두는 방식이 아니라, 클라이언트가 들고 온 토큰을 서버가 검증하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 API 서버를 여러 대로 늘릴 때 유리한 점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 토큰을 즉시 폐기하는 처리는 세션보다 까다로울 수 있다. 예를 들어 사용자가 로그아웃했는데 이미 발급된 Access Token이 아직 만료 전이라면, 서버가 별도 차단 목록을 관리하지 않는 한 토큰은 계속 유효할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분 때문에 실무에서는 Access Token을 짧게 두고 Refresh Token을 함께 설계하는 경우가 많다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;JWT 안에는 민감한 정보를 넣지 않는다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JWT는 서명된 토큰이지, 기본적으로 내용을 숨기는 토큰이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 토큰 안의 payload는 디코딩해서 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이런 값은 넣지 않는 게 좋다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비밀번호&lt;/li&gt;
&lt;li&gt;전화번호&lt;/li&gt;
&lt;li&gt;주민등록번호&lt;/li&gt;
&lt;li&gt;결제 정보&lt;/li&gt;
&lt;li&gt;내부 관리자 권한 설명&lt;/li&gt;
&lt;li&gt;민감한 개인정보&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 최소한의 식별자만 넣는 편이 안전하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 다음 정도다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;sub&quot;: &quot;kim&quot;,
  &quot;exp&quot;: 1760000000
}&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;code&gt;role&lt;/code&gt;이나 &lt;code&gt;scope&lt;/code&gt;를 넣기도 하지만, 이 역시 서비스 구조에 따라 신중하게 설계해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;SECRET_KEY는 코드에 그대로 두지 않는다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제에서는 설명을 위해 &lt;code&gt;SECRET_KEY&lt;/code&gt;를 코드 안에 적었다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;SECRET_KEY = &quot;change-this-secret-key&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GitHub에 코드가 올라가거나 서버 접근 권한이 잘못 관리되면 시크릿 키가 노출될 수 있다. 시크릿 키가 노출되면 공격자가 정상 서명을 가진 토큰을 만들 가능성이 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 프로젝트에서는 환경 변수를 사용하는 방식이 일반적이다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import os

SECRET_KEY = os.getenv(&quot;JWT_SECRET_KEY&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;code&gt;.env&lt;/code&gt; 파일을 쓴다면 해당 파일은 Git에 올라가지 않도록 &lt;code&gt;.gitignore&lt;/code&gt;에 추가해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Access Token 만료 시간은 짧게 잡는다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Access Token은 API 접근 권한을 가진 토큰이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 토큰이 길게 살아 있으면 탈취됐을 때 피해 범위가 커진다. 그래서 보통 Access Token은 짧게, Refresh Token은 상대적으로 길게 설정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 이 글에서는 흐름을 단순하게 보기 위해 Access Token만 다뤘다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인 유지, 자동 재로그인, 토큰 재발급까지 구현하려면 Refresh Token 구조가 추가로 필요하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;실제 서비스에서는 무엇을 더 추가해야 할까&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금 예시는 JWT 로그인 흐름을 이해하기 위한 최소 구조다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 서비스에 적용하려면 다음 요소를 추가로 고려해야 한다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;이유&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;데이터베이스 연동&lt;/td&gt;
&lt;td&gt;사용자 정보를 메모리가 아니라 DB에서 관리해야 함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;비밀번호 해싱 정책&lt;/td&gt;
&lt;td&gt;bcrypt, argon2 등 안전한 해싱 방식 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;환경 변수 관리&lt;/td&gt;
&lt;td&gt;SECRET_KEY, DB URL 같은 민감 정보 보호&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Refresh Token&lt;/td&gt;
&lt;td&gt;로그인 유지와 토큰 재발급 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTPS&lt;/td&gt;
&lt;td&gt;네트워크 구간에서 토큰 탈취 방지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;토큰 폐기 전략&lt;/td&gt;
&lt;td&gt;로그아웃, 계정 정지, 비밀번호 변경 시 대응&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;권한 관리&lt;/td&gt;
&lt;td&gt;일반 사용자와 관리자 API 분리&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;먼저 로그인 요청 &amp;rarr; 토큰 발급 &amp;rarr; 보호된 API 접근 흐름을 잡고, 그 다음 DB 연동과 Refresh Token으로 확장하는 편이 이해하기 쉽다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;마무리 기준&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI JWT 로그인은 코드보다 흐름을 먼저 이해하는 게 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 세 가지다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 로그인하면 서버가 JWT를 발급한다. 클라이언트는 이후 요청마다 &lt;code&gt;Authorization: Bearer ...&lt;/code&gt; 헤더에 토큰을 담아 보낸다. 서버는 토큰의 서명, 만료 시간, 사용자 식별 정보, 계정 상태를 확인한 뒤 API 접근을 허용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조만 잡히면 &lt;code&gt;OAuth2PasswordBearer&lt;/code&gt;, &lt;code&gt;OAuth2PasswordRequestForm&lt;/code&gt;, &lt;code&gt;jwt.encode()&lt;/code&gt;, &lt;code&gt;jwt.decode()&lt;/code&gt;가 각각 어디에 쓰이는지도 자연스럽게 연결된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 구현할 때는 Access Token 하나로 흐름을 익히고, 실제 프로젝트에서는 DB 연동, 환경 변수, Refresh Token, HTTPS, 토큰 폐기 전략까지 차례로 붙이는 방식이 적절하다.&lt;/p&gt;</description>
      <category>개발/FastAPI</category>
      <category>fastapi</category>
      <category>JWT</category>
      <category>Python</category>
      <category>백엔드</category>
      <category>웹보안</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/55</guid>
      <comments>https://notebase.tistory.com/entry/fastapi-jwt-login-token-auth-flow#entry55comment</comments>
      <pubDate>Fri, 29 May 2026 08:29:55 +0900</pubDate>
    </item>
    <item>
      <title>AI 코딩 에이전트 코드 검토 체크리스트: 초보자가 그대로 실행하기 전에 확인할 것</title>
      <link>https://notebase.tistory.com/entry/ai-coding-agent-code-review-checklist</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Codex, Claude Code, Cursor 같은 AI 코딩 도구가 만든 코드를 그대로 실행하기 전, 초보자가 반드시 확인해야 할 코드 검토 기준을 정리했습니다.&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;AI 코딩 도구를 쓰면 개발 속도는 확실히 빨라집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex, Claude Code, Cursor, GitHub Copilot 같은 도구는 코드를 대신 작성해 주고, 오류를 고쳐 주고, 때로는 여러 파일을 한 번에 수정하기도 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 여기서 중요한 문제가 하나 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AI가 만든 코드라고 해서 무조건 안전한 코드는 아닙니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 개발을 막 시작한 초보자라면 AI가 제안한 코드가 맞는지, 위험한지, 내 프로젝트에 적절한지 판단하기 어려울 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 AI 코딩 에이전트가 만든 코드를 그대로 실행하기 전에 초보자가 반드시 확인해야 할 기준을 정리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. 먼저 AI가 어떤 파일을 바꿨는지 확인하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 도구가 코드를 수정하면 가장 먼저 확인해야 할 것은 &lt;b&gt;변경된 파일 목록&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 코드가 잘 작동하는지만 보면 안 됩니다. 어떤 파일이 바뀌었는지부터 확인해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 파일이 수정되었다면 특히 주의해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;.env
package.json
requirements.txt
Dockerfile
docker-compose.yml
settings.py
config.py
auth.py
database.py&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일들은 프로젝트 설정, 인증, 데이터베이스 연결, 외부 패키지 설치와 관련된 경우가 많습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 잘못 수정되면 단순한 화면 오류가 아니라 보안 문제나 실행 환경 문제로 이어질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;초보자를 위한 기준&lt;/b&gt;&lt;br /&gt;내가 모르는 설정 파일이 바뀌었다면 바로 실행하지 말고, 변경 내용을 먼저 확인해야 합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Git diff로 변경 내용 확인하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 코드를 수정했다면 &lt;code&gt;git diff&lt;/code&gt; 명령어로 변경 내용을 확인하는 습관을 들이는 것이 좋습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git diff&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;초보자에게는 처음에 조금 낯설 수 있지만, AI 코드 검토에서는 가장 중요한 기본 도구입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 부분을 확인할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;- DEBUG = False
+ DEBUG = True&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;또는 다음처럼 API 주소가 바뀌었을 수도 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-diff&quot;&gt;- API_URL = &quot;https://api.example.com&quot;
+ API_URL = &quot;http://localhost:8000&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;초보자를 위한 기준&lt;/b&gt;&lt;br /&gt;AI가 바꾼 코드가 많을수록 실행 전에 &lt;code&gt;git diff&lt;/code&gt;를 먼저 확인해야 합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. 기능 브랜치에서 먼저 테스트하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 만든 코드를 바로 메인 브랜치에 반영하는 것은 위험할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가능하면 별도 브랜치를 만들어 테스트하는 것이 좋습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git checkout -b feature/ai-code-review&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;이렇게 하면 AI가 만든 코드에 문제가 있더라도 기존 작업을 비교적 안전하게 보호할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 후 문제가 없다면 그때 메인 브랜치에 병합하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보자 입장에서는 브랜치가 번거롭게 느껴질 수 있지만, AI 코딩 도구를 사용할수록 브랜치 사용은 더 중요해집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI는 한 번에 여러 파일을 바꿀 수 있기 때문입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. 민감 정보가 코드에 들어갔는지 확인하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 도구를 사용할 때 가장 조심해야 할 부분 중 하나는 &lt;b&gt;민감 정보 노출&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 값이 코드에 직접 들어가 있으면 위험합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;API_KEY
SECRET_KEY
ACCESS_TOKEN
DATABASE_URL
PRIVATE_KEY
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;예를 들어 AI가 다음과 같은 코드를 생성했다면 그대로 커밋하면 안 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;OPENAI_API_KEY = &quot;sk-xxxxxxxxxxxxxxxx&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;API 키나 비밀번호는 코드에 직접 넣지 않고, 보통 &lt;code&gt;.env&lt;/code&gt; 파일이나 환경 변수로 관리해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import os

OPENAI_API_KEY = os.getenv(&quot;OPENAI_API_KEY&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;만약 실수로 GitHub에 키를 올렸다면 단순히 코드를 지우는 것으로 끝나지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GitHub에는 민감 정보가 올라왔을 때 자동으로 감지하거나 경고하는 시스템이 있지만, 이미 노출된 키는 안전하다고 보기 어렵습니다.&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;초보자를 위한 기준&lt;/b&gt;&lt;br /&gt;비밀번호, API 키, 토큰처럼 보이는 값이 코드에 직접 들어가 있으면 커밋하지 않아야 합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;5. 외부 패키지를 새로 설치하는지 확인하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 도구는 문제를 해결하기 위해 새로운 패키지를 설치하라고 제안할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 명령어를 제안할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;pip install some-package
npm install some-package&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;하지만 AI가 제안한 패키지가 실제로 존재하는지, 신뢰할 수 있는지, 프로젝트에 꼭 필요한지 확인해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI는 가끔 실제로 존재하지 않는 패키지 이름을 만들어내기도 합니다. 이를 &lt;b&gt;AI 할루시네이션&lt;/b&gt;이라고 부릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 그럴듯해 보이는 패키지 이름을 제안했지만, 실제 공식 저장소에는 없을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 악의적인 사람이 유명 패키지와 비슷한 이름의 패키지를 만들어 배포하는 경우도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 패키지를 설치하기 전에는 다음을 확인하는 것이 좋습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;패키지 이름이 정확한가?
공식 문서나 저장소가 있는가?
다운로드 수나 유지보수 상태가 지나치게 이상하지 않은가?
내가 해결하려는 문제에 꼭 필요한가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;초보자를 위한 기준&lt;/b&gt;&lt;br /&gt;AI가 설치하라고 한 패키지는 바로 설치하지 말고, 이름과 출처를 먼저 확인해야 합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;6. 명령어 실행 전에 의미 확인하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 에이전트는 코드를 작성하는 것뿐 아니라 터미널 명령어를 제안하기도 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 명령어가 나올 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;rm -rf node_modules
npm install&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정도는 흔히 사용하는 명령어일 수 있지만, 초보자에게는 위험하게 느껴질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 다음과 같은 명령어는 바로 실행하지 않는 것이 좋습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;rm -rf /
sudo rm -rf
chmod -R 777
curl ... | bash
docker system prune -a&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;이 명령어들은 시스템 파일 삭제, 권한 변경, 외부 스크립트 실행, Docker 리소스 삭제 등으로 이어질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 제안했다고 해서 모든 명령어를 그대로 실행하면 안 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 전에 최소한 다음을 확인해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;무엇을 삭제하는 명령어인가?
어떤 권한을 바꾸는 명령어인가?
외부 URL에서 스크립트를 받아 실행하는가?
되돌릴 수 있는 작업인가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;초보자를 위한 기준&lt;/b&gt;&lt;br /&gt;명령어의 의미를 설명할 수 없다면 바로 실행하지 않는 것이 안전합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;7. 인증과 권한 관련 코드는 더 꼼꼼히 보기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인, 회원가입, 권한 검사, 토큰 검증과 관련된 코드는 특히 주의해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 AI가 다음과 같은 코드를 만들 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;def is_admin(user):
    return True&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;또는 인증이 필요한 API에서 권한 검사가 빠질 수도 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;@app.get(&quot;/admin/users&quot;)
def get_users():
    return users&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;이 코드가 관리자만 접근해야 하는 API라면, 인증과 권한 확인 로직이 반드시 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보자는 인증 코드가 복잡하게 느껴질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 최소한 다음 질문은 해봐야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;로그인하지 않은 사용자도 접근할 수 있는가?
일반 사용자가 관리자 기능에 접근할 수 있는가?
토큰 검증이 실제로 수행되는가?
임시 테스트 코드가 남아 있지 않은가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;초보자를 위한 기준&lt;/b&gt;&lt;br /&gt;로그인, 권한, 토큰 관련 코드는 작동 여부보다 보안 흐름을 먼저 확인해야 합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;8. 데이터 삭제&amp;middot;수정 로직 확인하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 만든 코드 중 데이터베이스를 수정하거나 삭제하는 코드는 반드시 조심해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 코드는 위험할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;DELETE FROM users;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건 없이 실행되면 사용자 데이터가 전부 삭제될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 다음과 같은 코드도 주의해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;User.objects.all().delete()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 데이터 삭제용으로 생성된 코드가 실제 데이터베이스에 연결되면 큰 문제가 생길 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 삭제하거나 수정하는 코드를 볼 때는 다음을 확인해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;삭제 조건이 있는가?
실제 운영 데이터베이스에 연결되어 있지 않은가?
실행 전에 백업이 필요한 작업인가?
테스트 코드가 운영 코드에 섞여 있지 않은가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;초보자를 위한 기준&lt;/b&gt;&lt;br /&gt;데이터 삭제 코드는 한 줄이라도 반드시 의심하고 확인해야 합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;9. 테스트 코드 또는 실행 결과 확인하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 만든 코드가 보기에는 그럴듯해도 실제로 작동하지 않을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 가능하면 테스트를 실행해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python 프로젝트라면 다음과 같은 명령어를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;pytest&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;Node.js 프로젝트라면 다음과 같은 명령어를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm test&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 코드가 없다면 최소한 직접 실행해 보면서 주요 기능이 깨지지 않았는지 확인해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인할 항목은 다음과 같습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&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;AI가 만든 코드가 컴파일되거나 실행된다고 해서 항상 올바른 것은 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과와 기능 흐름까지 확인해야 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;10. 코드가 너무 많이 바뀌었다면 나눠서 요청하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 에이전트가 한 번에 너무 많은 파일을 수정하면 검토하기 어려워집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보자에게는 특히 위험합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경 범위가 크면 무엇이 문제를 일으켰는지 찾기 어렵기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴 때는 AI에게 작업을 더 작게 나눠서 요청하는 것이 좋습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;한 번에 전체를 수정하지 말고, 로그인 오류만 먼저 수정해줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;UI 수정은 제외하고 API 응답 형식만 변경해줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&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;AI를 잘 쓰는 핵심은 많은 코드를 한 번에 맡기는 것이 아니라, &lt;b&gt;검토 가능한 크기로 작업을 나누는 것&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;초보자를 위한 기준&lt;/b&gt;&lt;br /&gt;내가 한 번에 이해할 수 없는 변경량이라면 AI에게 작업 범위를 줄이라고 요청해야 합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;11. AI에게 변경 이유를 설명하게 하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 코드를 수정한 뒤에는 단순히 결과만 확인하지 말고, 왜 그렇게 바꿨는지 설명하게 하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음처럼 요청할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;방금 수정한 파일별로 변경 이유를 설명해줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;이 코드가 기존 코드와 비교해서 어떤 문제를 해결하는지 알려줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&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;이렇게 질문하면 AI가 만든 코드를 이해하는 데 도움이 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 AI의 설명이 애매하거나 앞뒤가 맞지 않는다면, 해당 코드는 다시 확인해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 설명하지 못하는 코드는 사람도 검토하기 어렵습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;12. 초보자를 위한 최종 체크리스트&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 도구가 만든 코드를 실행하기 전에는 아래 항목을 확인해 보세요.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;□ 어떤 파일이 바뀌었는지 확인했다.
□ git diff로 변경 내용을 확인했다.
□ 기능 브랜치에서 먼저 테스트했다.
□ API 키, 비밀번호, 토큰이 코드에 직접 들어가지 않았다.
□ 새로 설치하는 패키지의 이름과 출처를 확인했다.
□ 터미널 명령어의 의미를 이해했다.
□ 인증, 권한, 토큰 관련 코드가 안전한지 확인했다.
□ 데이터 삭제&amp;middot;수정 로직에 조건이 있는지 확인했다.
□ 테스트를 실행하거나 주요 기능을 직접 확인했다.
□ 변경 범위가 너무 크면 AI에게 작업을 나눠 달라고 요청했다.
□ AI에게 변경 이유를 설명하게 했다.&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;이 체크리스트를 모두 지키면 AI가 만든 코드를 훨씬 안전하게 검토할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;마무리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 에이전트는 개발을 빠르게 만들어 주는 강력한 도구입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 AI가 만든 코드를 그대로 믿고 실행하는 습관은 위험합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 초보자일수록 AI가 작성한 코드의 의미를 완전히 이해하지 못한 상태에서 승인하거나 실행할 가능성이 높습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 도구를 안전하게 사용하려면 중요한 기준은 하나입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AI에게 코드를 맡기더라도, 최종 책임은 실행하는 사람에게 있습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 AI가 만든 코드는 항상 변경 파일, 보안 정보, 외부 패키지, 명령어, 데이터 삭제 로직을 확인한 뒤 실행해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;초보자를 위한 핵심 요약&lt;/b&gt;&lt;br /&gt;이 코드가 무엇을 바꾸는지 모르면 바로 승인하지 않는다.&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <category>AI/AI 코딩</category>
      <category>AI코딩</category>
      <category>claudecode</category>
      <category>codex</category>
      <category>CURSOR</category>
      <category>githubcopilot</category>
      <category>코드리뷰</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/54</guid>
      <comments>https://notebase.tistory.com/entry/ai-coding-agent-code-review-checklist#entry54comment</comments>
      <pubDate>Thu, 28 May 2026 17:25:03 +0900</pubDate>
    </item>
    <item>
      <title>FastAPI 프로젝트 구조 잡는 법: main.py 하나에서 벗어나기</title>
      <link>https://notebase.tistory.com/entry/fastapi-project-structure</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI 프로젝트가 커질 때 main.py 하나로 관리하기 어려워집니다. APIRouter, schemas, models, database 파일을 어떤 기준으로 나누면 좋은지 초보자 눈높이에서 정리합니다.&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;FastAPI 프로젝트 구조는 처음부터 복잡하게 잡을 필요는 없습니다. 다만 API가 늘어나고 데이터베이스, 요청 모델, 응답 모델이 생기면 &lt;code&gt;main.py&lt;/code&gt; 하나로 관리하기 어려워집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 한 파일로 시작해도 괜찮습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 어느 순간 이런 문제가 생깁니다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;main.py 안에
- API 코드
- Pydantic 모델
- DB 연결 코드
- 테이블 모델
- 예외 처리
- 비즈니스 로직
이 전부 섞여 있음&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;br /&gt;새로운 API를 추가할 때마다 &lt;code&gt;main.py&lt;/code&gt;를 계속 열어야 하고, 어디에 무엇이 있는지도 헷갈립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI 공식 문서에서도 작은 예제는 한 파일로 작성할 수 있지만, 실제 웹 API를 만들다 보면 여러 파일로 나누는 경우가 많다고 설명합니다. 이때 핵심이 되는 기능이 &lt;code&gt;APIRouter&lt;/code&gt;입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;처음에는 main.py 하나로도 충분하다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입문 단계에서는 아래처럼 작성해도 문제 없습니다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;fastapi-basic/
└── main.py&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;예를 들어 간단한 API만 만들 때는 &lt;code&gt;main.py&lt;/code&gt; 하나가 더 이해하기 쉽습니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from fastapi import FastAPI

app = FastAPI()


@app.get(&quot;/&quot;)
def read_root():
    return {&quot;message&quot;: &quot;Hello FastAPI&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보자가 처음부터 아래처럼 구조를 만들 필요는 없습니다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;app/
├── api/
├── core/
├── db/
├── models/
├── schemas/
├── services/
└── repositories/&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;br /&gt;다만 아직 API가 몇 개 없는 상태라면 과한 구조입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 구조는 &amp;ldquo;처음부터 멋있게&amp;rdquo;가 아니라, 코드가 많아졌을 때 &amp;ldquo;덜 헷갈리게&amp;rdquo; 만드는 도구에 가깝습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;FastAPI 프로젝트는 언제 나눠야 할까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 상황이 생기면 파일 분리를 고민할 만합니다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;- API 주소가 5개 이상으로 늘어남
- users, items, posts처럼 기능이 나뉘기 시작함
- Pydantic 모델이 많아짐
- 데이터베이스 연결 코드가 들어감
- 인증, 예외 처리, 설정값 관리가 필요해짐
- main.py가 너무 길어져서 스크롤이 많아짐&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;code&gt;main.py&lt;/code&gt;에서 아래 코드들이 한꺼번에 보이기 시작하면 분리할 시점입니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;app = FastAPI()

class UserCreate(BaseModel):
    ...

class UserResponse(BaseModel):
    ...

engine = create_engine(...)

@app.get(&quot;/users&quot;)
def get_users():
    ...

@app.post(&quot;/users&quot;)
def create_user():
    ...&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;br /&gt;하지만 프로젝트가 커질수록 수정하기 어려워집니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;입문자에게 적당한 FastAPI 프로젝트 구조&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 파일을 나눠본다면 아래 정도가 적당합니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;fastapi-project/
├── app/
│   ├── __init__.py
│   ├── main.py
│   ├── database.py
│   ├── models.py
│   ├── schemas.py
│   └── routers/
│       ├── __init__.py
│       └── users.py
└── requirements.txt&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;이 구조는 너무 복잡하지 않으면서도, FastAPI 프로젝트에서 자주 등장하는 역할을 분리해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI 공식 문서의 &amp;ldquo;Bigger Applications&amp;rdquo; 예시도 &lt;code&gt;app/main.py&lt;/code&gt;, &lt;code&gt;dependencies.py&lt;/code&gt;, &lt;code&gt;routers/&lt;/code&gt; 같은 방식으로 파일을 나누는 흐름을 보여줍니다. 특히 &lt;code&gt;routers&lt;/code&gt; 폴더 안에 기능별 API 파일을 두는 방식이 핵심입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;각 파일의 역할&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 구조에서 각 파일은 대략 이런 역할을 합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;app/main.py           &amp;rarr; FastAPI 앱 생성, 라우터 등록
app/database.py       &amp;rarr; 데이터베이스 연결 설정
app/models.py         &amp;rarr; DB 테이블 모델
app/schemas.py        &amp;rarr; 요청/응답 데이터 모델
app/routers/users.py  &amp;rarr; users 관련 API&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;main.py: 앱의 시작점&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;main.py&lt;/code&gt;는 FastAPI 앱이 시작되는 파일입니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from fastapi import FastAPI
from app.routers import users

app = FastAPI()

app.include_router(users.router)


@app.get(&quot;/&quot;)
def read_root():
    return {&quot;message&quot;: &quot;FastAPI project structure&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;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;app.include_router(users.router)&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;code&gt;users.py&lt;/code&gt;에 작성한 API를 &lt;code&gt;main.py&lt;/code&gt;에 연결하는 코드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &lt;code&gt;main.py&lt;/code&gt;는 모든 API 코드를 직접 들고 있는 파일이 아닙니다.&lt;br /&gt;여러 라우터를 모아서 앱에 등록하는 역할을 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;routers/users.py: 기능별 API 파일&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;routers/users.py&lt;/code&gt;에는 사용자와 관련된 API를 작성합니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;from fastapi import APIRouter

router = APIRouter(
    prefix=&quot;/users&quot;,
    tags=[&quot;users&quot;]
)


@router.get(&quot;/&quot;)
def get_users():
    return [
        {&quot;id&quot;: 1, &quot;name&quot;: &quot;kim&quot;},
        {&quot;id&quot;: 2, &quot;name&quot;: &quot;lee&quot;}
    ]


@router.get(&quot;/{user_id}&quot;)
def get_user(user_id: int):
    return {&quot;id&quot;: user_id, &quot;name&quot;: &quot;kim&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;code&gt;FastAPI()&lt;/code&gt; 대신 &lt;code&gt;APIRouter()&lt;/code&gt;를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;router = APIRouter(
    prefix=&quot;/users&quot;,
    tags=[&quot;users&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;code&gt;prefix=&quot;/users&quot;&lt;/code&gt;를 지정했기 때문에 아래 API는&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;@router.get(&quot;/&quot;)
def get_users():
    ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로는 다음 주소로 동작합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;GET /users/&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;그리고 아래 API는&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;@router.get(&quot;/{user_id}&quot;)
def get_user(user_id: int):
    ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 주소로 동작합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;GET /users/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;br /&gt;사용자 API는 &lt;code&gt;users.py&lt;/code&gt;, 게시글 API는 &lt;code&gt;posts.py&lt;/code&gt;, 상품 API는 &lt;code&gt;products.py&lt;/code&gt;처럼 기능별로 나눌 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI의 &lt;code&gt;APIRouter&lt;/code&gt;는 이런 식으로 여러 API를 모듈 단위로 나눌 때 사용하는 도구입니다. Flask를 써본 사람이라면 Blueprint와 비슷한 역할로 이해할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;prefix와 슬래시 경로를 헷갈리지 말자&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 한 가지 주의할 점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;prefix=&quot;/users&quot;&lt;/code&gt;를 지정한 상태에서 라우터 내부 경로를 어떻게 쓰느냐에 따라 실제 주소가 조금 달라질 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;router = APIRouter(prefix=&quot;/users&quot;)


@router.get(&quot;&quot;)
def get_users():
    return []&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 경로는 보통 아래처럼 이해하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;GET /users&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 아래처럼 작성하면&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;router = APIRouter(prefix=&quot;/users&quot;)


@router.get(&quot;/&quot;)
def get_users():
    return []&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경로는 다음처럼 됩니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;GET /users/&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;br /&gt;다만 프로젝트 안에서 기준을 섞으면 헷갈릴 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 어떤 파일에서는 &lt;code&gt;@router.get(&quot;&quot;)&lt;/code&gt;를 쓰고, 다른 파일에서는 &lt;code&gt;@router.get(&quot;/&quot;)&lt;/code&gt;를 쓰면 주소 끝의 슬래시 때문에 예상과 다르게 동작한다고 느낄 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입문 단계에서는 아래처럼 하나의 기준을 정해두는 편이 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;목록 조회 API는 @router.get(&quot;/&quot;) 형태로 작성한다.
prefix는 /users, /posts처럼 기능 단위로 지정한다.&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;code&gt;@router.get(&quot;/&quot;)&lt;/code&gt; 방식을 기준으로 설명합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;schemas.py: 요청과 응답 모델 분리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;schemas.py&lt;/code&gt;에는 Pydantic 모델을 작성합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;from pydantic import BaseModel


class UserCreate(BaseModel):
    name: str
    email: str


class UserResponse(BaseModel):
    id: int
    name: str
    email: str&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;이 파일은 API에서 주고받는 데이터 모양을 정의하는 곳입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 회원가입 API가 있다면 요청 데이터는 이런 형태일 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;kim&quot;,
  &quot;email&quot;: &quot;kim@example.com&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;code&gt;UserCreate&lt;/code&gt; 모델을 사용하면 FastAPI가 요청 데이터의 구조를 검사해 줍니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;@router.post(&quot;/&quot;)
def create_user(user: UserCreate):
    return user&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;code&gt;schemas.py&lt;/code&gt;가 데이터베이스 테이블을 정의하는 파일은 아니라는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;schemas.py&lt;/code&gt;는 API 요청과 응답의 모양을 담당합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;models.py: 데이터베이스 테이블 모델&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;models.py&lt;/code&gt;에는 데이터베이스 테이블과 연결되는 모델을 작성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 SQLAlchemy를 사용한다면 이런 코드가 들어갈 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;from sqlalchemy import Column, Integer, String
from app.database import Base


class User(Base):
    __tablename__ = &quot;users&quot;

    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, nullable=False)
    email = Column(String, unique=True, index=True, nullable=False)&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;code&gt;schemas.py&lt;/code&gt;와 &lt;code&gt;models.py&lt;/code&gt;를 헷갈리기 쉽습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단히 구분하면 이렇습니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;파일&lt;/th&gt;
&lt;th&gt;역할&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;schemas.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;API 요청/응답 데이터 구조&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;models.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;데이터베이스 테이블 구조&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로는 둘 다 &amp;ldquo;모델&amp;rdquo;처럼 보이기 때문에 초보자가 자주 헷갈립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 역할이 다릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;schemas.py&lt;/code&gt;는 클라이언트와 주고받는 데이터에 가깝고,&lt;br /&gt;&lt;code&gt;models.py&lt;/code&gt;는 데이터베이스에 저장되는 구조에 가깝습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;database.py: DB 연결 설정&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;database.py&lt;/code&gt;에는 데이터베이스 연결 코드를 둡니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;from sqlalchemy import create_engine
from sqlalchemy.orm import declarative_base, sessionmaker

DATABASE_URL = &quot;sqlite:///./test.db&quot;

engine = create_engine(
    DATABASE_URL,
    connect_args={&quot;check_same_thread&quot;: False}
)

SessionLocal = sessionmaker(
    autocommit=False,
    autoflush=False,
    bind=engine
)

Base = declarative_base()&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;SQLite를 사용할 때는 위처럼 &lt;code&gt;sqlite:///./test.db&lt;/code&gt; 형태로 DB 파일을 지정할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI 공식 SQL 데이터베이스 문서에서도 입문 예제에서는 SQLite를 사용합니다. SQLite는 하나의 파일로 동작하고, Python에서 기본적으로 지원하기 때문에 예제와 실습에 적합합니다. 다만 실제 운영 환경에서는 PostgreSQL 같은 데이터베이스 서버를 사용할 수 있다고 안내합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 건 DB 연결 코드를 &lt;code&gt;main.py&lt;/code&gt;에 직접 넣지 않는다는 점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB 설정은 프로젝트 전체에서 재사용될 가능성이 높습니다.&lt;br /&gt;따라서 &lt;code&gt;database.py&lt;/code&gt;처럼 따로 분리하는 편이 관리하기 쉽습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;전체 코드 흐름으로 보면 더 쉽다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 구조를 흐름으로 보면 이렇습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;main.py
  └── users.router 등록

routers/users.py
  └── /users API 작성

schemas.py
  └── 요청/응답 데이터 구조 정의

models.py
  └── DB 테이블 구조 정의

database.py
  └── DB 연결 설정&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;code&gt;main.py&lt;/code&gt;가 모든 일을 하는 구조에서 벗어나 각 파일이 역할을 나눠 갖는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 나누면 코드를 찾기 쉬워집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 API를 수정하고 싶으면 &lt;code&gt;routers/users.py&lt;/code&gt;를 보면 됩니다.&lt;br /&gt;요청 데이터 구조를 수정하고 싶으면 &lt;code&gt;schemas.py&lt;/code&gt;를 보면 됩니다.&lt;br /&gt;DB 연결 설정을 바꾸고 싶으면 &lt;code&gt;database.py&lt;/code&gt;를 보면 됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;기능이 더 많아지면 어떻게 나눌까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트가 조금 더 커지면 &lt;code&gt;routers&lt;/code&gt; 안에 파일을 추가할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;app/
├── main.py
├── database.py
├── models.py
├── schemas.py
└── routers/
    ├── __init__.py
    ├── users.py
    ├── posts.py
    └── comments.py&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;code&gt;main.py&lt;/code&gt;에서 각각 등록합니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;from fastapi import FastAPI
from app.routers import users, posts, comments

app = FastAPI()

app.include_router(users.router)
app.include_router(posts.router)
app.include_router(comments.router)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 라우터 파일은 자기 기능만 담당합니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;users.py     &amp;rarr; 사용자 API
posts.py     &amp;rarr; 게시글 API
comments.py  &amp;rarr; 댓글 API&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;더 큰 프로젝트에서는 기능별 폴더로 나누기도 한다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트가 더 커지면 아래처럼 기능별로 폴더를 나누는 방식도 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;app/
├── main.py
├── database.py
└── users/
    ├── router.py
    ├── schemas.py
    ├── models.py
    └── service.py&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 여러 기능을 이렇게 나눌 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;app/
├── main.py
├── database.py
├── users/
│   ├── router.py
│   ├── schemas.py
│   └── service.py
├── posts/
│   ├── router.py
│   ├── schemas.py
│   └── service.py
└── comments/
    ├── router.py
    ├── schemas.py
    └── service.py&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;예를 들어 사용자 기능이 복잡해져서 사용자 API, 사용자 요청 모델, 사용자 서비스 로직이 많아졌다면 &lt;code&gt;users/&lt;/code&gt; 폴더로 묶는 편이 낫습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 처음부터 이 구조로 시작할 필요는 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입문 단계에서는 오히려 파일을 찾는 데 시간이 더 걸릴 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;services.py는 언제 필요할까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보자 프로젝트에서는 처음부터 &lt;code&gt;services.py&lt;/code&gt;를 만들지 않아도 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 API 함수 안에 로직이 길어지면 분리하는 편이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래 코드는 API 함수가 너무 많은 일을 합니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;@router.post(&quot;/&quot;)
def create_user(user: UserCreate):
    # 이메일 중복 확인
    # 비밀번호 암호화
    # DB 저장
    # 응답 데이터 생성
    return result&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 경우에는 실제 처리 로직을 서비스 함수로 분리할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;@router.post(&quot;/&quot;)
def create_user(user: UserCreate):
    return user_service.create_user(user)&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;그러면 API 파일은 요청을 받고 응답을 돌려주는 역할에 집중할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 이 단계는 프로젝트가 어느 정도 커졌을 때 고민해도 늦지 않습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;dependencies.py는 언제 필요할까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI에는 의존성 주입 기능이 있습니다.&lt;br /&gt;FastAPI의 Dependency Injection 시스템은 다른 구성 요소를 쉽게 통합하고 재사용할 수 있도록 설계되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 DB 세션을 여러 API에서 반복해서 사용한다면 아래처럼 의존성 함수를 만들 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from app.database import SessionLocal


def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()&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;code&gt;dependencies.py&lt;/code&gt;에 둘 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;app/
├── main.py
├── database.py
├── dependencies.py
└── routers/
    └── users.py&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;그리고 API에서 이렇게 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;from fastapi import Depends
from sqlalchemy.orm import Session
from app.dependencies import get_db


@router.get(&quot;/&quot;)
def get_users(db: Session = Depends(get_db)):
    return []&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;처음에는 낯설 수 있지만, DB 연결이나 인증 처리처럼 여러 API에서 반복되는 코드를 줄일 때 유용합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;__init__.py는 왜 필요할까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;폴더 구조 예시를 보면 &lt;code&gt;__init__.py&lt;/code&gt; 파일이 자주 보입니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;app/
├── __init__.py
└── routers/
    ├── __init__.py
    └── users.py&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;code&gt;__init__.py&lt;/code&gt;는 해당 폴더를 Python 패키지로 인식하게 해주는 파일입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 Python에서는 일부 상황에서 없어도 동작할 수 있지만, 입문자라면 일단 만들어두는 편이 덜 헷갈립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 아래처럼 import를 할 때 도움이 됩니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;from app.routers import users&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 안에는 아무 코드가 없어도 됩니다.&lt;/p&gt;
&lt;pre class=&quot;vala&quot;&gt;&lt;code&gt;# 비워둬도 됩니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;초보자가 피하면 좋은 구조&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI 프로젝트를 처음 나눌 때 흔히 하는 실수가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;너무 빨리 구조를 복잡하게 만드는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 API가 3개뿐인데 아래처럼 나누면 오히려 불편합니다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;app/
├── api/
│   └── v1/
│       └── endpoints/
├── core/
├── crud/
├── db/
├── models/
├── schemas/
├── services/
├── repositories/
└── utils/&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;ldquo;코드를 어디에 넣어야 하지?&amp;rdquo;라는 고민만 늘어납니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 아래 정도면 충분합니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;app/
├── main.py
├── database.py
├── models.py
├── schemas.py
└── routers/
    └── users.py&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;br /&gt;수정하기 쉬운 코드를 만들기 위한 최소한의 정리입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;추천하는 단계별 구조&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 최종 구조를 정하려고 하지 말고, 단계별로 확장하는 편이 좋습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1단계: 가장 작은 구조&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;fastapi-project/
└── main.py&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;간단한 API 테스트나 학습용 예제에 적합합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2단계: 라우터 분리&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;fastapi-project/
├── app/
│   ├── main.py
│   └── routers/
│       └── users.py
└── requirements.txt&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;API가 기능별로 나뉘기 시작할 때 적합합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3단계: DB와 모델 분리&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;fastapi-project/
├── app/
│   ├── main.py
│   ├── database.py
│   ├── models.py
│   ├── schemas.py
│   └── routers/
│       └── users.py
└── requirements.txt&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;SQLite, SQLAlchemy, Pydantic 모델이 들어가는 CRUD 프로젝트에 적합합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4단계: 기능별 폴더 구조&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;fastapi-project/
├── app/
│   ├── main.py
│   ├── database.py
│   ├── users/
│   │   ├── router.py
│   │   ├── schemas.py
│   │   └── service.py
│   └── posts/
│       ├── router.py
│       ├── schemas.py
│       └── service.py
└── requirements.txt&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;FastAPI 프로젝트 구조를 잡을 때 기준&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일을 나눌 때는 이름보다 기준이 더 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 기준으로 판단하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;main.py
&amp;rarr; 앱 생성과 라우터 등록만 남긴다.

routers/
&amp;rarr; API 주소별 처리 코드를 둔다.

schemas.py
&amp;rarr; 요청/응답 데이터 모델을 둔다.

models.py
&amp;rarr; DB 테이블 모델을 둔다.

database.py
&amp;rarr; DB 연결 설정을 둔다.

dependencies.py
&amp;rarr; 여러 API에서 반복해서 쓰는 의존성 함수를 둔다.

services.py
&amp;rarr; API 안에서 처리하기엔 긴 비즈니스 로직을 둔다.&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;예를 들어 DB를 쓰지 않는 프로젝트라면 &lt;code&gt;database.py&lt;/code&gt;, &lt;code&gt;models.py&lt;/code&gt;는 필요 없습니다.&lt;br /&gt;서비스 로직이 거의 없다면 &lt;code&gt;services.py&lt;/code&gt;도 없어도 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요할 때 추가하면 됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;처음 구조는 단순한 게 좋다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI 프로젝트 구조에 정답은 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 입문자라면 아래 구조부터 시작하는 게 가장 무난합니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;app/
├── main.py
├── database.py
├── models.py
├── schemas.py
└── routers/
    └── users.py&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;이 구조만 이해해도 FastAPI 프로젝트를 한 파일에서 벗어나 관리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 기준은 하나입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일을 많이 나누는 것이 좋은 구조가 아니라, 코드를 찾고 고치기 쉬운 구조가 좋은 구조입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API가 적으면 &lt;code&gt;main.py&lt;/code&gt; 하나로 시작해도 됩니다.&lt;br /&gt;API가 늘어나면 &lt;code&gt;routers&lt;/code&gt;로 나누면 됩니다.&lt;br /&gt;DB와 요청 모델이 생기면 &lt;code&gt;database.py&lt;/code&gt;, &lt;code&gt;models.py&lt;/code&gt;, &lt;code&gt;schemas.py&lt;/code&gt;를 분리하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 실무형 구조를 따라 하기보다, 프로젝트가 커지는 속도에 맞춰 한 단계씩 나누는 편이 더 안전합니다.&lt;/p&gt;</description>
      <category>개발/FastAPI</category>
      <category>fastapi</category>
      <category>백엔드</category>
      <category>코딩입문</category>
      <category>파이썬</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/53</guid>
      <comments>https://notebase.tistory.com/entry/fastapi-project-structure#entry53comment</comments>
      <pubDate>Wed, 27 May 2026 13:23:23 +0900</pubDate>
    </item>
    <item>
      <title>FastAPI CRUD API 만들기: GET, POST, PUT, DELETE 한 번에 이해하기</title>
      <link>https://notebase.tistory.com/entry/fastapi-simple-crud-api</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI로 간단한 CRUD API를 만드는 방법을 예제로 정리했습니다. GET, POST, PUT, DELETE 요청이 각각 어떤 역할을 하는지 코드와 함께 확인할 수 있습니다.&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;FastAPI CRUD API를 처음 만들 때는 데이터베이스부터 연결하기보다, 메모리 데이터를 사용해 흐름을 먼저 잡는 게 좋습니다. &lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;가 각각 어떤 역할을 하는지 코드로 바로 확인할 수 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 예제에서는 상품 정보를 다루는 간단한 CRUD API를 만들어보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스는 아직 사용하지 않습니다.&lt;br /&gt;파이썬 딕셔너리에 데이터를 저장하는 방식으로 API 구조만 먼저 익힙니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;CRUD API란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CRUD는 데이터를 다룰 때 가장 기본이 되는 4가지 작업입니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CRUD&lt;/th&gt;
&lt;th&gt;의미&lt;/th&gt;
&lt;th&gt;HTTP 메서드&lt;/th&gt;
&lt;th&gt;예시&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Create&lt;/td&gt;
&lt;td&gt;생성&lt;/td&gt;
&lt;td&gt;POST&lt;/td&gt;
&lt;td&gt;상품 추가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read&lt;/td&gt;
&lt;td&gt;조회&lt;/td&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;td&gt;상품 목록 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Update&lt;/td&gt;
&lt;td&gt;수정&lt;/td&gt;
&lt;td&gt;PUT&lt;/td&gt;
&lt;td&gt;상품 정보 수정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete&lt;/td&gt;
&lt;td&gt;삭제&lt;/td&gt;
&lt;td&gt;DELETE&lt;/td&gt;
&lt;td&gt;상품 삭제&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 백엔드 API를 만들면 대부분 이 흐름에서 시작합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 쇼핑몰 상품 API라면 다음과 같은 기능이 필요합니다.&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;li&gt;상품 삭제&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 바로 CRUD API입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;만들 API 구조&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서 만들 API는 아래와 같습니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;기능&lt;/th&gt;
&lt;th&gt;메서드&lt;/th&gt;
&lt;th&gt;경로&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;전체 상품 조회&lt;/td&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/items&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;특정 상품 조회&lt;/td&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/items/{item_id}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;상품 생성&lt;/td&gt;
&lt;td&gt;POST&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/items&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;상품 수정&lt;/td&gt;
&lt;td&gt;PUT&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/items/{item_id}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;상품 삭제&lt;/td&gt;
&lt;td&gt;DELETE&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/items/{item_id}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;{item_id}&lt;/code&gt;는 경로 파라미터입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &lt;code&gt;/items/1&lt;/code&gt;로 요청하면 ID가 &lt;code&gt;1&lt;/code&gt;인 상품을 조회합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;프로젝트 준비&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 프로젝트 폴더를 만들고 이동합니다.&lt;/p&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;mkdir fastapi-crud-example
cd fastapi-crud-example&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;br /&gt;여러 파이썬 프로젝트를 같이 다루다 보면 패키지 버전이 섞여 문제가 생길 수 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;macOS 또는 Linux에서는 아래처럼 실행합니다.&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;python -m venv .venv
source .venv/bin/activate&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;Windows에서는 아래 명령어를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;python -m venv .venv
.venv\Scripts\activate&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;그다음 FastAPI와 실행 서버인 Uvicorn을 설치합니다.&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;pip install fastapi uvicorn&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;code&gt;main.py&lt;/code&gt; 파일을 만듭니다.&lt;/p&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;fastapi-crud-example/
└── main.py&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;FastAPI는 파이썬 타입 힌트를 기반으로 API를 만들 수 있는 웹 프레임워크입니다.&lt;br /&gt;입문 단계에서는 타입 힌트가 낯설 수 있지만, FastAPI에서는 이 타입 정보가 요청 데이터 검증과 자동 문서화에 같이 활용됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;기본 코드 작성하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;main.py&lt;/code&gt;에 먼저 기본 구조를 작성합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

app = FastAPI()&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;code&gt;FastAPI()&lt;/code&gt;는 애플리케이션 객체입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;HTTPException&lt;/code&gt;은 요청한 데이터가 없거나 잘못된 요청이 들어왔을 때 HTTP 상태 코드를 반환하기 위해 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;BaseModel&lt;/code&gt;은 요청 데이터의 구조를 정의할 때 사용합니다.&lt;br /&gt;FastAPI에서는 요청 본문을 받을 때 Pydantic 모델을 사용해 데이터 구조를 선언할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;상품 모델 만들기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 예제에서는 상품에 &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;price&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt; 값을 넣겠습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class Item(BaseModel):
    name: str
    price: int
    description: str | None = None&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;이 예제는 Python 3.10 이상 문법을 기준으로 작성했습니다.&lt;br /&gt;&lt;code&gt;str | None&lt;/code&gt;은 &amp;ldquo;문자열이거나 None일 수 있다&amp;rdquo;는 뜻입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python 3.9 이하를 사용한다면 아래처럼 &lt;code&gt;Optional&lt;/code&gt;을 사용하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;from typing import Optional

class Item(BaseModel):
    name: str
    price: int
    description: Optional[str] = None&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;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;필드&lt;/th&gt;
&lt;th&gt;타입&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;상품 이름&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;price&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;int&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;상품 가격&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;description&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;str | None&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;상품 설명, 없어도 됨&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;description&lt;/code&gt;에는 기본값으로 &lt;code&gt;None&lt;/code&gt;을 넣었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 상품을 생성할 때 설명은 생략할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;Keyboard&quot;,
  &quot;price&quot;: 50000
}&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;임시 데이터 저장소 만들기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 데이터베이스를 연결하지 않으므로 파이썬 딕셔너리를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;items = {
    1: {
        &quot;name&quot;: &quot;Keyboard&quot;,
        &quot;price&quot;: 50000,
        &quot;description&quot;: &quot;Mechanical keyboard&quot;
    },
    2: {
        &quot;name&quot;: &quot;Mouse&quot;,
        &quot;price&quot;: 30000,
        &quot;description&quot;: &quot;Wireless mouse&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 SQLite, PostgreSQL 같은 데이터베이스를 사용합니다.&lt;br /&gt;하지만 지금 단계에서는 CRUD 흐름을 이해하는 게 목적이므로 메모리 데이터로 충분합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;items&lt;/code&gt; 딕셔너리의 키는 &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;2&lt;/code&gt;처럼 정수입니다.&lt;br /&gt;뒤에서 API 응답을 보면 &lt;code&gt;&quot;1&quot;&lt;/code&gt;, &lt;code&gt;&quot;2&quot;&lt;/code&gt;처럼 문자열로 보이는데, 이건 오류가 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSON 객체의 키는 문자열로 표현되기 때문에 FastAPI가 응답을 JSON으로 변환하는 과정에서 그렇게 표시됩니다.&lt;br /&gt;파이썬 코드 안에서는 여전히 &lt;code&gt;items[1]&lt;/code&gt;처럼 정수 키로 접근합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;전체 상품 조회 API 만들기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 전체 상품을 조회하는 API입니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;@app.get(&quot;/items&quot;)
def get_items():
    return items&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 서버를 실행합니다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;uvicorn main:app --reload&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저에서 아래 주소로 접속합니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://127.0.0.1:8000/items&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;그러면 현재 저장된 상품 목록이 JSON 형태로 반환됩니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;1&quot;: {
    &quot;name&quot;: &quot;Keyboard&quot;,
    &quot;price&quot;: 50000,
    &quot;description&quot;: &quot;Mechanical keyboard&quot;
  },
  &quot;2&quot;: {
    &quot;name&quot;: &quot;Mouse&quot;,
    &quot;price&quot;: 30000,
    &quot;description&quot;: &quot;Wireless mouse&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;여기서 중요한 점은 API의 응답이 HTML 페이지가 아니라 JSON 데이터라는 점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백엔드 API는 보통 프론트엔드, 모바일 앱, 다른 서버가 데이터를 가져갈 수 있도록 JSON을 반환합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;특정 상품 조회 API 만들기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 상품 ID를 받아서 하나의 상품만 조회하는 API를 만듭니다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;@app.get(&quot;/items/{item_id}&quot;)
def get_item(item_id: int):
    if item_id not in items:
        raise HTTPException(status_code=404, detail=&quot;Item not found&quot;)

    return items[item_id]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청 예시는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;GET /items/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;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;Keyboard&quot;,
  &quot;price&quot;: 50000,
  &quot;description&quot;: &quot;Mechanical keyboard&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;code&gt;/items/{item_id}&lt;/code&gt;의 &lt;code&gt;{item_id}&lt;/code&gt;는 함수의 &lt;code&gt;item_id&lt;/code&gt; 매개변수로 전달됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주소로 들어오는 값은 원래 문자열입니다.&lt;br /&gt;하지만 함수에서 &lt;code&gt;item_id: int&lt;/code&gt;로 선언했기 때문에 FastAPI가 정수로 변환해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &lt;code&gt;/items/1&lt;/code&gt;로 요청하면 &lt;code&gt;&quot;1&quot;&lt;/code&gt;이라는 문자열이 들어오지만, 함수 안에서는 정수 &lt;code&gt;1&lt;/code&gt;로 다룰 수 있습니다.&lt;br /&gt;만약 &lt;code&gt;/items/abc&lt;/code&gt;처럼 숫자로 바꿀 수 없는 값이 들어오면 FastAPI가 자동으로 검증 오류를 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;존재하지 않는 ID를 요청하면 &lt;code&gt;404 Not Found&lt;/code&gt;를 반환합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;GET /items/999&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응답 예시는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;detail&quot;: &quot;Item not found&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;code&gt;&quot;없음&quot;&lt;/code&gt; 같은 문자열을 반환하지 않고 &lt;code&gt;404&lt;/code&gt; 상태 코드를 반환하는 게 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API를 사용하는 쪽에서 &amp;ldquo;요청은 성공했지만 데이터가 없는 것인지&amp;rdquo;, &amp;ldquo;요청한 리소스 자체가 없는 것인지&amp;rdquo;를 구분할 수 있기 때문입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;상품 생성 API 만들기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상품을 새로 추가할 때는 &lt;code&gt;POST&lt;/code&gt;를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;@app.post(&quot;/items&quot;)
def create_item(item: Item):
    new_id = max(items.keys()) + 1 if items else 1

    items[new_id] = item.model_dump()

    return {
        &quot;id&quot;: new_id,
        &quot;item&quot;: items[new_id]
    }&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;code&gt;item: Item&lt;/code&gt; 부분이 요청 본문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트가 아래와 같은 JSON을 보내면 FastAPI가 &lt;code&gt;Item&lt;/code&gt; 모델에 맞는지 자동으로 확인합니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;Monitor&quot;,
  &quot;price&quot;: 200000,
  &quot;description&quot;: &quot;27 inch monitor&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;정상 요청이면 새 ID와 함께 생성된 상품이 반환됩니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;id&quot;: 3,
  &quot;item&quot;: {
    &quot;name&quot;: &quot;Monitor&quot;,
    &quot;price&quot;: 200000,
    &quot;description&quot;: &quot;27 inch monitor&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;code&gt;item.model_dump()&lt;/code&gt;는 Pydantic 모델을 파이썬 딕셔너리로 바꾸는 코드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pydantic v2 기준에서는 예전의 &lt;code&gt;.dict()&lt;/code&gt;보다 &lt;code&gt;.model_dump()&lt;/code&gt;를 사용하는 방식이 더 자연스럽습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;상품 수정 API 만들기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 상품을 수정할 때는 &lt;code&gt;PUT&lt;/code&gt;을 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;@app.put(&quot;/items/{item_id}&quot;)
def update_item(item_id: int, item: Item):
    if item_id not in items:
        raise HTTPException(status_code=404, detail=&quot;Item not found&quot;)

    items[item_id] = item.model_dump()

    return {
        &quot;id&quot;: item_id,
        &quot;item&quot;: items[item_id]
    }&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;code&gt;/items/{item_id}&lt;/code&gt;의 &lt;code&gt;{item_id}&lt;/code&gt;가 함수의 &lt;code&gt;item_id: int&lt;/code&gt;로 연결됩니다.&lt;br /&gt;특정 상품 조회 API와 같은 방식으로 FastAPI가 경로 값을 정수로 변환하고 검증합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청 예시는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;PUT /items/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;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;Gaming Keyboard&quot;,
  &quot;price&quot;: 80000,
  &quot;description&quot;: &quot;RGB mechanical keyboard&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;그러면 ID가 &lt;code&gt;1&lt;/code&gt;인 상품 정보가 새 데이터로 바뀝니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;id&quot;: 1,
  &quot;item&quot;: {
    &quot;name&quot;: &quot;Gaming Keyboard&quot;,
    &quot;price&quot;: 80000,
    &quot;description&quot;: &quot;RGB mechanical keyboard&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;PUT&lt;/code&gt;은 보통 &amp;ldquo;전체 수정&amp;rdquo;에 가깝게 사용합니다.&lt;br /&gt;즉, 상품 이름만 바꾸고 싶더라도 전체 필드를 다시 보내는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일부 필드만 수정하려면 &lt;code&gt;PATCH&lt;/code&gt;를 따로 설계하는 경우가 많습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 CRUD 기본 구조에 집중하기 위해 &lt;code&gt;PUT&lt;/code&gt;만 사용하겠습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;상품 삭제 API 만들기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상품을 삭제할 때는 &lt;code&gt;DELETE&lt;/code&gt;를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;@app.delete(&quot;/items/{item_id}&quot;)
def delete_item(item_id: int):
    if item_id not in items:
        raise HTTPException(status_code=404, detail=&quot;Item not found&quot;)

    deleted_item = items.pop(item_id)

    return {
        &quot;message&quot;: &quot;Item deleted&quot;,
        &quot;item&quot;: deleted_item
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청 예시는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;DELETE /items/2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응답은 이렇게 나옵니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;message&quot;: &quot;Item deleted&quot;,
  &quot;item&quot;: {
    &quot;name&quot;: &quot;Mouse&quot;,
    &quot;price&quot;: 30000,
    &quot;description&quot;: &quot;Wireless mouse&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;code&gt;/items&lt;/code&gt;를 조회하면 ID가 &lt;code&gt;2&lt;/code&gt;인 상품은 사라진 상태입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 지금은 메모리 데이터를 사용하므로 서버를 재시작하면 처음 코드에 적어둔 데이터로 다시 돌아갑니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;전체 코드&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 지금까지 작성한 전체 코드입니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    price: int
    description: str | None = None


items = {
    1: {
        &quot;name&quot;: &quot;Keyboard&quot;,
        &quot;price&quot;: 50000,
        &quot;description&quot;: &quot;Mechanical keyboard&quot;
    },
    2: {
        &quot;name&quot;: &quot;Mouse&quot;,
        &quot;price&quot;: 30000,
        &quot;description&quot;: &quot;Wireless mouse&quot;
    }
}


@app.get(&quot;/items&quot;)
def get_items():
    return items


@app.get(&quot;/items/{item_id}&quot;)
def get_item(item_id: int):
    if item_id not in items:
        raise HTTPException(status_code=404, detail=&quot;Item not found&quot;)

    return items[item_id]


@app.post(&quot;/items&quot;)
def create_item(item: Item):
    new_id = max(items.keys()) + 1 if items else 1

    items[new_id] = item.model_dump()

    return {
        &quot;id&quot;: new_id,
        &quot;item&quot;: items[new_id]
    }


@app.put(&quot;/items/{item_id}&quot;)
def update_item(item_id: int, item: Item):
    if item_id not in items:
        raise HTTPException(status_code=404, detail=&quot;Item not found&quot;)

    items[item_id] = item.model_dump()

    return {
        &quot;id&quot;: item_id,
        &quot;item&quot;: items[item_id]
    }


@app.delete(&quot;/items/{item_id}&quot;)
def delete_item(item_id: int):
    if item_id not in items:
        raise HTTPException(status_code=404, detail=&quot;Item not found&quot;)

    deleted_item = items.pop(item_id)

    return {
        &quot;message&quot;: &quot;Item deleted&quot;,
        &quot;item&quot;: deleted_item
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 실행 명령어는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;uvicorn main:app --reload&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Swagger UI에서 테스트하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI의 편한 점 중 하나는 API 문서가 자동으로 생성된다는 점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버를 실행한 뒤 아래 주소로 접속합니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://127.0.0.1:8000/docs&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;그러면 Swagger UI 화면이 열립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 직접 &lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt; 요청을 테스트할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보자 입장에서는 Postman 같은 별도 도구 없이도 API 요청을 바로 테스트할 수 있어서 실습하기 좋습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;테스트 순서 추천&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 실습할 때는 아래 순서대로 테스트해 보면 흐름이 잘 보입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;GET /items&lt;/code&gt;로 전체 상품 조회&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /items/1&lt;/code&gt;로 특정 상품 조회&lt;/li&gt;
&lt;li&gt;&lt;code&gt;POST /items&lt;/code&gt;로 새 상품 생성&lt;/li&gt;
&lt;li&gt;다시 &lt;code&gt;GET /items&lt;/code&gt;로 상품이 추가됐는지 확인&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PUT /items/1&lt;/code&gt;로 상품 수정&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DELETE /items/2&lt;/code&gt;로 상품 삭제&lt;/li&gt;
&lt;li&gt;다시 &lt;code&gt;GET /items&lt;/code&gt;로 삭제 결과 확인&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 순서대로 해보면 CRUD가 단순히 코드 조각이 아니라 하나의 데이터 흐름이라는 점이 보입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;이 예제에서 주의할 점&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 예제는 CRUD API 구조를 이해하기 위한 입문용 코드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 서비스 코드와는 차이가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫째, 데이터가 메모리에 저장됩니다.&lt;br /&gt;서버를 재시작하면 데이터가 초기화됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘째, ID 생성 방식이 단순합니다.&lt;br /&gt;&lt;code&gt;max(items.keys()) + 1&lt;/code&gt; 방식은 혼자 실습할 때는 괜찮지만, 여러 요청이 동시에 들어오는 실제 서비스에서는 적절하지 않습니다.&lt;/p&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;넷째, 데이터베이스 연결이 없습니다.&lt;br /&gt;운영 환경에서는 SQLite, PostgreSQL, MySQL 같은 데이터베이스와 연결해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 입문 단계에서는 이 방식이 더 낫습니다.&lt;br /&gt;처음부터 데이터베이스, 세션, ORM까지 같이 넣으면 CRUD의 핵심 흐름이 흐려질 수 있기 때문입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;마무리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI CRUD API의 기본 흐름은 복잡하지 않습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;GET&lt;/code&gt;으로 조회한다&lt;/li&gt;
&lt;li&gt;&lt;code&gt;POST&lt;/code&gt;로 생성한다&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PUT&lt;/code&gt;으로 수정한다&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DELETE&lt;/code&gt;로 삭제한다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 네 가지 구조만 제대로 이해해도 API 설계의 기본 뼈대는 잡을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 단계로 넘어간다면 같은 예제를 SQLite나 SQLModel과 연결해 보는 것이 좋습니다.&lt;br /&gt;그때부터는 서버를 재시작해도 데이터가 유지되고, 실제 백엔드 프로젝트에 가까운 구조로 확장할 수 있습니다.&lt;/p&gt;</description>
      <category>개발/FastAPI</category>
      <category>개발도구</category>
      <category>코딩입문</category>
      <category>파이썬</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/52</guid>
      <comments>https://notebase.tistory.com/entry/fastapi-simple-crud-api#entry52comment</comments>
      <pubDate>Wed, 27 May 2026 08:52:05 +0900</pubDate>
    </item>
    <item>
      <title>FastAPI SQLite 연결하기: SQLModel로 DB 저장 API 만들기</title>
      <link>https://notebase.tistory.com/entry/fastapi-sqlite-sqlmodel-guide</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI에서 SQLite를 연결하는 방법을 SQLModel 기준으로 정리합니다. 최신 &lt;code&gt;lifespan&lt;/code&gt; 방식으로 테이블을 생성하고, 데이터 저장&amp;middot;조회&amp;middot;삭제 API까지 초보자도 따라 할 수 있게 설명합니다.&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;FastAPI SQLite 연결은 API가 받은 데이터를 메모리에만 두지 않고 실제 DB 파일에 저장하는 첫 단계입니다. 입문 단계에서는 별도 DB 서버가 필요 없는 SQLite로 시작하면 구조를 이해하기 쉽습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI에서 데이터베이스를 연결하는 방법은 여러 가지입니다.&lt;br /&gt;SQLAlchemy를 직접 써도 되고, 비동기 DB 라이브러리를 써도 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 처음 배우는 단계라면 &lt;code&gt;SQLModel&lt;/code&gt;을 사용하는 방식이 편합니다. SQLModel은 Pydantic과 SQLAlchemy를 기반으로 만들어진 라이브러리라서, FastAPI의 요청&amp;middot;응답 모델과 DB 테이블 모델을 비슷한 문법으로 다룰 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 &lt;b&gt;2026년 5월 기준 FastAPI 공식 문서 흐름에 맞춰&lt;/b&gt; FastAPI + SQLite + SQLModel 조합으로 간단한 메모 API를 만들어봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 변경점도 하나 있습니다. 예전 FastAPI 예제에서는 &lt;code&gt;@app.on_event(&quot;startup&quot;)&lt;/code&gt;을 자주 사용했지만, 현재는 앱 시작과 종료 시 실행할 작업을 &lt;code&gt;lifespan&lt;/code&gt;으로 처리하는 방식이 권장됩니다. 이 글도 새로 작성하는 코드에 맞춰 &lt;code&gt;lifespan&lt;/code&gt; 기준으로 설명합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;FastAPI에서 SQLite를 연결한다는 뜻&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI만 사용하면 보통 이런 식으로 데이터를 다룹니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;items = []&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트나 딕셔너리에 데이터를 넣는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 테스트할 때는 편하지만 문제가 있습니다.&lt;br /&gt;서버를 껐다 켜면 데이터가 사라집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQLite를 연결하면 데이터가 &lt;code&gt;.db&lt;/code&gt; 파일에 저장됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래처럼 파일이 생깁니다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;app.db&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;이 파일 안에 테이블이 만들어지고, API로 등록한 데이터가 저장됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흐름으로 보면 아래와 같습니다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;API 요청
&amp;darr;
FastAPI 라우터
&amp;darr;
SQLModel Session
&amp;darr;
SQLite DB 파일&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;code&gt;Session&lt;/code&gt;은 FastAPI 코드와 데이터베이스 사이에서 실제 저장, 조회, 삭제 작업을 처리하는 통로라고 보면 됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;SQLite를 처음 DB로 쓰기 좋은 이유&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQLite는 별도 서버 설치가 필요 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL이나 PostgreSQL은 DB 서버를 실행하고, 계정과 비밀번호를 만들고, 포트 설정도 해야 합니다.&lt;br /&gt;반면 SQLite는 파일 하나로 동작합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 FastAPI 입문 단계에서는 SQLite가 적당합니다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;SQLite
- 별도 DB 서버 설치 필요 없음
- 로컬 실습에 적합
- 작은 프로젝트나 테스트용으로 편함
- 파일 기반 DB라 구조를 이해하기 쉬움&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;물론 모든 서비스에 SQLite가 맞는 것은 아닙니다.&lt;br /&gt;동시에 많은 사용자가 쓰는 서비스, 복잡한 운영 환경, 대규모 트래픽이 필요한 서비스라면 PostgreSQL이나 MySQL을 고려하는 게 일반적입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 FastAPI에서 DB 연결 구조를 처음 익히는 목적이라면 SQLite로 충분합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;SQLModel을 사용하는 이유&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI를 배우다 보면 이런 이름들을 자주 보게 됩니다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;Pydantic
SQLAlchemy
SQLModel&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 헷갈릴 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단히 나누면 이렇습니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;도구&lt;/th&gt;
&lt;th&gt;역할&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pydantic&lt;/td&gt;
&lt;td&gt;API 요청/응답 데이터 검증&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQLAlchemy&lt;/td&gt;
&lt;td&gt;파이썬에서 SQL DB를 다루는 ORM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQLModel&lt;/td&gt;
&lt;td&gt;Pydantic + SQLAlchemy를 FastAPI에 맞게 쉽게 사용하도록 만든 도구&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;여기서 중요한 점은 SQLModel이 완전히 새로운 DB 기술은 아니라는 것입니다.&lt;br /&gt;SQLModel은 SQLAlchemy와 Pydantic 위에서 동작합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입문자 입장에서는 장점이 큽니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB 테이블 모델과 API 데이터 모델을 비슷한 문법으로 작성할 수 있기 때문입니다.&lt;br /&gt;처음부터 SQLAlchemy의 모든 개념을 깊게 들어가기보다, FastAPI에서 DB가 연결되는 흐름을 먼저 익히기에 좋습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;프로젝트 구조 만들기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 프로젝트 폴더를 하나 만듭니다.&lt;/p&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;mkdir fastapi-sqlite-example
cd fastapi-sqlite-example&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가상환경을 만듭니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;python -m venv .venv&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;macOS 또는 Linux라면 아래 명령어로 활성화합니다.&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;source .venv/bin/activate&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;Windows PowerShell이라면 아래처럼 실행합니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;.venv\Scripts\Activate.ps1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 구조는 단순하게 시작합니다.&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;fastapi-sqlite-example/
├─ main.py
└─ app.db   # 실행 후 자동 생성&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;br /&gt;DB 연결 구조를 익힌 뒤에 &lt;code&gt;database.py&lt;/code&gt;, &lt;code&gt;models.py&lt;/code&gt;, &lt;code&gt;routers/&lt;/code&gt;로 나누는 편이 낫습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;필요한 패키지 설치하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI, Uvicorn, SQLModel을 설치합니다.&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;pip install fastapi &quot;uvicorn[standard]&quot; sqlmodel&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;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;패키지&lt;/th&gt;
&lt;th&gt;역할&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;fastapi&lt;/td&gt;
&lt;td&gt;API 서버 프레임워크&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;uvicorn&lt;/td&gt;
&lt;td&gt;FastAPI 앱을 실행하는 ASGI 서버&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sqlmodel&lt;/td&gt;
&lt;td&gt;SQLite 같은 SQL DB를 파이썬 모델로 다루는 도구&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;code&gt;main.py&lt;/code&gt; 파일을 만듭니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;SQLite 연결 코드 작성하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 가장 기본적인 DB 연결 코드를 작성합니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;# main.py

from fastapi import FastAPI
from sqlmodel import SQLModel, Field, Session, create_engine

sqlite_file_name = &quot;app.db&quot;
sqlite_url = f&quot;sqlite:///{sqlite_file_name}&quot;

engine = create_engine(sqlite_url, echo=True)

app = FastAPI()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 핵심은 이 부분입니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;sqlite_url = f&quot;sqlite:///{sqlite_file_name}&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;code&gt;sqlite:///app.db&lt;/code&gt;는 현재 프로젝트 폴더에 &lt;code&gt;app.db&lt;/code&gt;라는 SQLite 파일을 사용하겠다는 뜻입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이 코드는 DB 연결 엔진을 만듭니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;engine = create_engine(sqlite_url, echo=True)&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;code&gt;echo=True&lt;/code&gt;를 넣으면 SQLModel이 내부적으로 실행하는 SQL 문이 터미널에 출력됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입문 단계에서는 켜두는 것이 좋습니다.&lt;br /&gt;API를 호출했을 때 어떤 SQL이 실행되는지 확인할 수 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 운영 환경에서는 로그가 너무 많아질 수 있으므로 상황에 따라 끕니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;engine = create_engine(sqlite_url, echo=False)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;데이터 모델 만들기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 DB에 저장할 모델을 만듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 간단한 메모 API를 예시로 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;# main.py

from fastapi import FastAPI
from sqlmodel import SQLModel, Field, Session, create_engine


sqlite_file_name = &quot;app.db&quot;
sqlite_url = f&quot;sqlite:///{sqlite_file_name}&quot;

engine = create_engine(sqlite_url, echo=True)

app = FastAPI()


class Memo(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    title: str
    content: str&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;이 클래스가 SQLite의 테이블이 됩니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class Memo(SQLModel, table=True):&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;code&gt;table=True&lt;/code&gt;가 붙어 있기 때문에 SQLModel은 이 클래스를 DB 테이블로 인식합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 필드는 테이블의 컬럼이 됩니다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;id: int | None = Field(default=None, primary_key=True)
title: str
content: str&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;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;필드&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;id&lt;/td&gt;
&lt;td&gt;메모 고유 번호&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;title&lt;/td&gt;
&lt;td&gt;메모 제목&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;content&lt;/td&gt;
&lt;td&gt;메모 내용&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;id&lt;/code&gt;는 처음 생성할 때 직접 넣지 않아도 됩니다.&lt;br /&gt;SQLite가 자동으로 값을 만들 수 있도록 &lt;code&gt;default=None&lt;/code&gt;으로 둡니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;앱 시작 시 테이블 생성하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델만 작성한다고 실제 DB 테이블이 생기지는 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테이블을 만들려면 아래 코드가 필요합니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;def create_db_and_tables():
    SQLModel.metadata.create_all(engine)&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;code&gt;SQLModel.metadata.create_all(engine)&lt;/code&gt;은 지금까지 &lt;code&gt;table=True&lt;/code&gt;로 정의한 모델을 기준으로 DB 테이블을 생성합니다.&lt;/p&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;이 작업은 앱이 시작될 때 한 번 실행되면 됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;code&gt;lifespan&lt;/code&gt;으로 시작 작업 등록하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전 FastAPI 예제에서는 아래와 같은 코드를 자주 볼 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;@app.on_event(&quot;startup&quot;)
def on_startup():
    create_db_and_tables()&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;code&gt;lifespan&lt;/code&gt; 방식으로 익혀두는 편이 좋습니다.&lt;br /&gt;앱 시작 시 실행할 작업은 &lt;code&gt;yield&lt;/code&gt; 위에 작성하고, 앱 종료 시 실행할 작업은 &lt;code&gt;yield&lt;/code&gt; 아래에 작성합니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from contextlib import asynccontextmanager

@asynccontextmanager
async def lifespan(app: FastAPI):
    create_db_and_tables()
    yield

app = FastAPI(lifespan=lifespan)&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;여기서는 서버가 시작될 때 SQLite 테이블을 생성해야 하므로 &lt;code&gt;create_db_and_tables()&lt;/code&gt;를 &lt;code&gt;yield&lt;/code&gt; 위에 둡니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 이 단계까지의 코드는 아래와 같습니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;# main.py

from contextlib import asynccontextmanager

from fastapi import FastAPI
from sqlmodel import SQLModel, Field, Session, create_engine


sqlite_file_name = &quot;app.db&quot;
sqlite_url = f&quot;sqlite:///{sqlite_file_name}&quot;

engine = create_engine(sqlite_url, echo=True)


class Memo(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    title: str
    content: str


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


@asynccontextmanager
async def lifespan(app: FastAPI):
    create_db_and_tables()
    yield


app = FastAPI(lifespan=lifespan)&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;code&gt;app.db&lt;/code&gt; 파일이 생성됩니다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;uvicorn main:app --reload&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;다만 아직 API 엔드포인트가 없기 때문에 저장이나 조회는 할 수 없습니다.&lt;br /&gt;이제 데이터를 등록하는 API를 추가해봅니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;데이터 저장 API 만들기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모를 저장하는 &lt;code&gt;POST&lt;/code&gt; API를 추가합니다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;@app.post(&quot;/memos/&quot;)
def create_memo(memo: Memo):
    with Session(engine) as session:
        session.add(memo)
        session.commit()
        session.refresh(memo)
        return memo&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;code&gt;Session&lt;/code&gt;, &lt;code&gt;commit()&lt;/code&gt;, &lt;code&gt;refresh()&lt;/code&gt;입니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;with Session(engine) as session:&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;code&gt;Session(engine)&lt;/code&gt;은 DB와 대화하기 위한 작업 단위를 만듭니다.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;session.add(memo)&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;code&gt;session.add(memo)&lt;/code&gt;는 저장할 객체를 세션에 추가합니다.&lt;br /&gt;아직 DB 파일에 최종 저장된 상태는 아닙니다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;session.commit()&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;code&gt;session.commit()&lt;/code&gt;입니다.&lt;br /&gt;이 코드를 실행해야 변경 내용이 실제 DB에 반영됩니다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;session.refresh(memo)&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;code&gt;session.refresh(memo)&lt;/code&gt;는 DB에 저장된 최신 값을 다시 가져옵니다.&lt;br /&gt;특히 &lt;code&gt;id&lt;/code&gt;처럼 DB가 자동으로 만든 값을 응답에 포함하려면 이 과정이 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 코드는 아래처럼 됩니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;# main.py

from contextlib import asynccontextmanager

from fastapi import FastAPI
from sqlmodel import SQLModel, Field, Session, create_engine


sqlite_file_name = &quot;app.db&quot;
sqlite_url = f&quot;sqlite:///{sqlite_file_name}&quot;

engine = create_engine(sqlite_url, echo=True)


class Memo(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    title: str
    content: str


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


@asynccontextmanager
async def lifespan(app: FastAPI):
    create_db_and_tables()
    yield


app = FastAPI(lifespan=lifespan)


@app.post(&quot;/memos/&quot;)
def create_memo(memo: Memo):
    with Session(engine) as session:
        session.add(memo)
        session.commit()
        session.refresh(memo)
        return memo&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;데이터 조회 API 만들기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저장한 메모 목록을 조회하는 API도 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQLModel에서는 &lt;code&gt;select()&lt;/code&gt;를 사용해 데이터를 조회할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;moonscript&quot;&gt;&lt;code&gt;from sqlmodel import select&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;상단 import를 수정합니다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;from sqlmodel import SQLModel, Field, Session, create_engine, select&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;그리고 조회 API를 추가합니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;@app.get(&quot;/memos/&quot;)
def read_memos():
    with Session(engine) as session:
        statement = select(Memo)
        memos = session.exec(statement).all()
        return memos&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 코드는 아래와 같습니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;# main.py

from contextlib import asynccontextmanager

from fastapi import FastAPI
from sqlmodel import SQLModel, Field, Session, create_engine, select


sqlite_file_name = &quot;app.db&quot;
sqlite_url = f&quot;sqlite:///{sqlite_file_name}&quot;

engine = create_engine(sqlite_url, echo=True)


class Memo(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    title: str
    content: str


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


@asynccontextmanager
async def lifespan(app: FastAPI):
    create_db_and_tables()
    yield


app = FastAPI(lifespan=lifespan)


@app.post(&quot;/memos/&quot;)
def create_memo(memo: Memo):
    with Session(engine) as session:
        session.add(memo)
        session.commit()
        session.refresh(memo)
        return memo


@app.get(&quot;/memos/&quot;)
def read_memos():
    with Session(engine) as session:
        statement = select(Memo)
        memos = session.exec(statement).all()
        return memos&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;단일 메모 조회 API 추가하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목록 조회만 있으면 조금 아쉽습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 &lt;code&gt;id&lt;/code&gt;로 하나의 메모만 조회하는 API를 추가합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;from fastapi import FastAPI, HTTPException&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;상단 import를 수정합니다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;from fastapi import FastAPI, HTTPException
from sqlmodel import SQLModel, Field, Session, create_engine, select&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 아래 코드를 추가합니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@app.get(&quot;/memos/{memo_id}&quot;)
def read_memo(memo_id: int):
    with Session(engine) as session:
        memo = session.get(Memo, memo_id)

        if memo is None:
            raise HTTPException(status_code=404, detail=&quot;Memo not found&quot;)

        return memo&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;code&gt;session.get(Memo, memo_id)&lt;/code&gt;는 기본키 기준으로 데이터를 찾습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터가 없으면 &lt;code&gt;None&lt;/code&gt;이 반환됩니다.&lt;br /&gt;이때 단순히 빈 값을 반환하기보다 &lt;code&gt;404 Not Found&lt;/code&gt;를 보내는 편이 API답습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;삭제 API 추가하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모를 삭제하는 API도 만들어봅니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;@app.delete(&quot;/memos/{memo_id}&quot;)
def delete_memo(memo_id: int):
    with Session(engine) as session:
        memo = session.get(Memo, memo_id)

        if memo is None:
            raise HTTPException(status_code=404, detail=&quot;Memo not found&quot;)

        session.delete(memo)
        session.commit()

        return {&quot;message&quot;: &quot;Memo deleted&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;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;id&lt;/code&gt;로 메모를 찾습니다.&lt;/li&gt;
&lt;li&gt;없으면 404를 반환합니다.&lt;/li&gt;
&lt;li&gt;있으면 삭제합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;session.commit()&lt;/code&gt;으로 DB에 반영합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;최종 코드&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 합치면 최종 코드는 아래와 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드만 복사해서 실행해도 흐름을 이해할 수 있도록 주요 구간마다 주석을 넣었습니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;# main.py

from contextlib import asynccontextmanager

from fastapi import FastAPI, HTTPException
from sqlmodel import SQLModel, Field, Session, create_engine, select


# 1. SQLite DB 연결 설정
sqlite_file_name = &quot;app.db&quot;
sqlite_url = f&quot;sqlite:///{sqlite_file_name}&quot;
engine = create_engine(sqlite_url, echo=True)


# 2. DB 테이블 모델 정의
class Memo(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    title: str
    content: str


# 3. 앱 시작 시 DB 테이블 생성
def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


@asynccontextmanager
async def lifespan(app: FastAPI):
    create_db_and_tables()
    yield


app = FastAPI(lifespan=lifespan)


# 4. 메모 생성 API
@app.post(&quot;/memos/&quot;)
def create_memo(memo: Memo):
    with Session(engine) as session:
        session.add(memo)
        session.commit()
        session.refresh(memo)
        return memo


# 5. 메모 목록 조회 API
@app.get(&quot;/memos/&quot;)
def read_memos():
    with Session(engine) as session:
        statement = select(Memo)
        memos = session.exec(statement).all()
        return memos


# 6. 단일 메모 조회 API
@app.get(&quot;/memos/{memo_id}&quot;)
def read_memo(memo_id: int):
    with Session(engine) as session:
        memo = session.get(Memo, memo_id)

        if memo is None:
            raise HTTPException(status_code=404, detail=&quot;Memo not found&quot;)

        return memo


# 7. 메모 삭제 API
@app.delete(&quot;/memos/{memo_id}&quot;)
def delete_memo(memo_id: int):
    with Session(engine) as session:
        memo = session.get(Memo, memo_id)

        if memo is None:
            raise HTTPException(status_code=404, detail=&quot;Memo not found&quot;)

        session.delete(memo)
        session.commit()

        return {&quot;message&quot;: &quot;Memo deleted&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;이 코드만으로도 간단한 SQLite 기반 메모 API가 동작합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;서버 실행하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널에서 실행합니다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;uvicorn main:app --reload&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;또는 FastAPI CLI를 사용한다면 아래처럼 실행할 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;fastapi dev main.py&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;실행 후 브라우저에서 Swagger 문서에 접속합니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://127.0.0.1:8000/docs&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;FastAPI는 자동으로 API 문서를 생성해줍니다.&lt;br /&gt;여기서 &lt;code&gt;POST /memos/&lt;/code&gt;, &lt;code&gt;GET /memos/&lt;/code&gt;, &lt;code&gt;GET /memos/{memo_id}&lt;/code&gt;, &lt;code&gt;DELETE /memos/{memo_id}&lt;/code&gt;를 직접 테스트할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Swagger에서 메모 저장 테스트하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;POST /memos/&lt;/code&gt;를 열고 &lt;code&gt;Try it out&lt;/code&gt;을 누릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청 Body에 아래처럼 입력합니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;title&quot;: &quot;FastAPI SQLite 연습&quot;,
  &quot;content&quot;: &quot;SQLModel을 사용해서 SQLite에 데이터를 저장해본다.&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;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;id&quot;: 1,
  &quot;title&quot;: &quot;FastAPI SQLite 연습&quot;,
  &quot;content&quot;: &quot;SQLModel을 사용해서 SQLite에 데이터를 저장해본다.&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;code&gt;id&lt;/code&gt;는 직접 입력하지 않았지만 DB 저장 후 자동으로 생성됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음 &lt;code&gt;GET /memos/&lt;/code&gt;를 실행하면 저장된 데이터가 목록으로 나옵니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[
  {
    &quot;id&quot;: 1,
    &quot;title&quot;: &quot;FastAPI SQLite 연습&quot;,
    &quot;content&quot;: &quot;SQLModel을 사용해서 SQLite에 데이터를 저장해본다.&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;code&gt;app.db&lt;/code&gt; 파일이 남아 있다면 데이터도 유지됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;자주 헷갈리는 부분&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. &lt;code&gt;app.db&lt;/code&gt; 파일이 안 보일 때&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버를 실행하기 전에는 &lt;code&gt;app.db&lt;/code&gt; 파일이 없을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일은 아래 코드가 실행되면서 생성됩니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;SQLModel.metadata.create_all(engine)&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. &lt;code&gt;session.commit()&lt;/code&gt;을 안 하면 저장되지 않는다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드만 쓰면 DB에 바로 저장되는 것처럼 보일 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;session.add(memo)&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;code&gt;session.commit()&lt;/code&gt;에서 일어납니다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;session.commit()&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;code&gt;add()&lt;/code&gt;만 하고 &lt;code&gt;commit()&lt;/code&gt;을 빼먹는 것입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. &lt;code&gt;session.refresh()&lt;/code&gt;는 왜 필요할까?&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;session.refresh(memo)&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;이 코드는 DB에 저장된 최신 상태를 다시 가져옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 &lt;code&gt;id&lt;/code&gt;처럼 DB가 자동으로 만든 값은 저장 전에는 알 수 없습니다.&lt;br /&gt;&lt;code&gt;session.refresh(memo)&lt;/code&gt;를 호출하면 방금 저장된 데이터의 &lt;code&gt;id&lt;/code&gt;까지 포함해서 응답할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. &lt;code&gt;lifespan&lt;/code&gt;에서 &lt;code&gt;yield&lt;/code&gt;는 왜 필요할까?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;lifespan&lt;/code&gt;은 앱의 시작과 종료 흐름을 하나의 함수에서 다룹니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;@asynccontextmanager
async def lifespan(app: FastAPI):
    create_db_and_tables()
    yield&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;code&gt;yield&lt;/code&gt; 위쪽은 앱이 시작될 때 실행됩니다.&lt;br /&gt;&lt;code&gt;yield&lt;/code&gt; 아래쪽은 앱이 종료될 때 실행됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예제에서는 종료 시 따로 처리할 작업이 없기 때문에 &lt;code&gt;yield&lt;/code&gt; 아래에는 아무 코드도 작성하지 않았습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5. SQLite 파일을 지우면 데이터도 사라진다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQLite는 파일 기반 DB입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예제에서는 데이터가 &lt;code&gt;app.db&lt;/code&gt;에 저장됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 &lt;code&gt;app.db&lt;/code&gt; 파일을 삭제하면 테이블과 데이터도 함께 사라집니다.&lt;br /&gt;실습 중 DB 상태가 꼬였을 때는 파일을 지우고 다시 실행하는 방식으로 초기화할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 실제 중요한 데이터가 들어 있는 DB 파일은 함부로 삭제하면 안 됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;SQLite를 계속 써도 되는 경우&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQLite는 단순 실습용으로만 쓰는 도구는 아닙니다.&lt;br /&gt;작은 규모의 앱이나 로컬 도구에서는 충분히 유용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 상황이라면 SQLite로 시작해도 괜찮습니다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;- FastAPI DB 연결을 처음 배우는 경우
- 개인 프로젝트를 만드는 경우
- 로컬에서만 사용하는 자동화 도구를 만드는 경우
- 관리자 한두 명만 쓰는 작은 내부 도구를 만드는 경우
- 테스트용 API 서버를 빠르게 만들고 싶은 경우&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;반대로 아래 상황이라면 PostgreSQL이나 MySQL 같은 DB를 검토하는 편이 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;- 여러 사용자가 동시에 많이 접속하는 서비스
- 운영 서버에서 안정적인 DB 관리가 필요한 경우
- 사용자 계정, 결제, 권한 같은 중요한 데이터를 다루는 경우
- 배포 환경에서 DB 백업과 마이그레이션이 중요한 경우&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;여기서 중요한 점은 SQLite가 나쁘다는 뜻이 아닙니다.&lt;br /&gt;역할이 다를 뿐입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI 입문 단계에서는 SQLite가 구조를 이해하기 좋고, 운영 서비스로 갈수록 더 강한 DB 관리 기능이 필요해집니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;마무리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI에서 SQLite를 연결할 때 핵심은 세 가지입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫째, &lt;code&gt;create_engine()&lt;/code&gt;으로 SQLite DB 파일과 연결합니다.&lt;br /&gt;둘째, &lt;code&gt;SQLModel&lt;/code&gt; 클래스로 테이블 구조를 만듭니다.&lt;br /&gt;셋째, &lt;code&gt;Session&lt;/code&gt;을 통해 데이터를 저장하고 조회합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 새로 작성하는 FastAPI 코드라면 앱 시작 작업은 &lt;code&gt;@app.on_event(&quot;startup&quot;)&lt;/code&gt;보다 &lt;code&gt;lifespan&lt;/code&gt; 방식으로 익혀두는 편이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 PostgreSQL, Alembic, 비동기 DB 세션까지 한 번에 넣으면 구조가 흐려질 수 있습니다.&lt;br /&gt;먼저 SQLite로 저장과 조회 흐름을 손에 익히고, 그다음 실제 서비스 구조로 넘어가는 편이 더 안정적입니다.&lt;/p&gt;</description>
      <category>개발/FastAPI</category>
      <category>Python</category>
      <category>개발도구</category>
      <category>파이썬</category>
      <category>파이썬기초</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/51</guid>
      <comments>https://notebase.tistory.com/entry/fastapi-sqlite-sqlmodel-guide#entry51comment</comments>
      <pubDate>Tue, 26 May 2026 17:45:01 +0900</pubDate>
    </item>
    <item>
      <title>개인정보처리방침</title>
      <link>https://notebase.tistory.com/pages/privacy-policy</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Notebase는 이용자의 개인정보를 중요하게 생각하며, 개인정보 보호 관련 법령을 준수하기 위해 다음과 같은 개인정보처리방침을 운영합니다.&lt;br /&gt;&lt;br /&gt;본 개인정보처리방침은 2026년 5월 20일부터 적용됩니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;1. 개인정보의 수집 항목 및 수집 방법&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;본&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;IP,&amp;nbsp;쿠키,&amp;nbsp;기기&amp;nbsp;정보&lt;br /&gt;-&amp;nbsp;수집&amp;nbsp;방법:&amp;nbsp;Google&amp;nbsp;Analytics,&amp;nbsp;Google&amp;nbsp;AdSense,&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;b&gt;2. 개인정보의 수집 및 이용 목적&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;수집된&amp;nbsp;정보는&amp;nbsp;다음의&amp;nbsp;목적을&amp;nbsp;위해&amp;nbsp;활용됩니다.&lt;br /&gt;&lt;br /&gt;-&amp;nbsp;방문자&amp;nbsp;통계&amp;nbsp;분석&lt;br /&gt;-&amp;nbsp;블로그&amp;nbsp;콘텐츠&amp;nbsp;및&amp;nbsp;사용자&amp;nbsp;경험&amp;nbsp;개선&lt;br /&gt;-&amp;nbsp;Google&amp;nbsp;AdSense&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;위한&amp;nbsp;참고&amp;nbsp;자료&lt;br /&gt;&lt;br /&gt;&lt;b&gt;3. 개인정보의 보유 및 이용 기간&lt;/b&gt;&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;있습니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;4. 개인정보의 제3자 제공&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;본&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;합니다.&lt;br /&gt;&lt;br /&gt;-&amp;nbsp;Google&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;-&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;5. 쿠키 사용 및 거부&lt;/b&gt;&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;있습니다.&lt;br /&gt;&lt;br /&gt;Google을&amp;nbsp;포함한&amp;nbsp;제3자&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;있습니다.&lt;br /&gt;&lt;br /&gt;쿠키&amp;nbsp;설정&amp;nbsp;거부&amp;nbsp;방법&amp;nbsp;예시(Chrome&amp;nbsp;기준):&lt;br /&gt;&lt;br /&gt;설정&amp;nbsp;&amp;gt;&amp;nbsp;개인정보&amp;nbsp;및&amp;nbsp;보안&amp;nbsp;&amp;gt;&amp;nbsp;서드&amp;nbsp;파티&amp;nbsp;쿠키&amp;nbsp;&amp;gt;&amp;nbsp;서드&amp;nbsp;파티&amp;nbsp;쿠키&amp;nbsp;차단&lt;br /&gt;&lt;br /&gt;또한&amp;nbsp;사용자는&amp;nbsp;Google&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;b&gt;6. 개인정보 보호 책임자&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;- 책임자: notebase&lt;br /&gt;- 이메일: harperthebest222@gmail.com&lt;br /&gt;-&amp;nbsp;문의&amp;nbsp;사항은&amp;nbsp;가능한&amp;nbsp;한&amp;nbsp;신속하고&amp;nbsp;성실하게&amp;nbsp;답변하겠습니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;7. 개인정보처리방침의 변경&lt;/b&gt;&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;공지합니다.&lt;br /&gt;&lt;br /&gt;본 개인정보처리방침은 2026년 5월 20일부터 적용됩니다.&lt;/p&gt;</description>
      <category>개발/FastAPI</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/pages/privacy-policy</guid>
      <pubDate>Tue, 26 May 2026 16:10:03 +0900</pubDate>
    </item>
    <item>
      <title>FastAPI Swagger 문서 자동 생성, `/docs` 화면이 만들어지는 원리</title>
      <link>https://notebase.tistory.com/entry/fastapi-swagger-auto-docs</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI에서 Swagger UI 문서가 자동으로 생성되는 원리를 초보자도 이해하기 쉽게 정리했습니다. &lt;code&gt;/docs&lt;/code&gt;, &lt;code&gt;/redoc&lt;/code&gt;, &lt;code&gt;/openapi.json&lt;/code&gt;의 역할과 실제 API 테스트 방법까지 함께 설명합니다.&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;FastAPI를 실행한 뒤 &lt;code&gt;/docs&lt;/code&gt;에 접속하면 API 문서 화면이 자동으로 열립니다. 따로 문서 파일을 만들지 않았는데도 엔드포인트, 요청값, 응답 구조가 정리되어 나오죠. 이 기능이 FastAPI의 Swagger 문서 자동 생성입니다.&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;ldquo;내가 한 건 함수 하나 만든 것뿐인데, 왜 문서가 생기지?&amp;rdquo;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분을 이해하면 FastAPI가 왜 입문자에게도 편하고, 협업용 API 서버를 만들 때 왜 자주 언급되는지 감이 잡힙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2026년 5월 기준 FastAPI 공식 문서에 따르면, FastAPI는 기본적으로 Swagger UI를 &lt;code&gt;/docs&lt;/code&gt;에서 제공하고, ReDoc을 &lt;code&gt;/redoc&lt;/code&gt;에서 제공합니다. OpenAPI 스키마는 기본적으로 &lt;code&gt;/openapi.json&lt;/code&gt;에서 확인할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;FastAPI에서 Swagger 문서가 자동으로 생긴다는 뜻&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API 문서는 보통 이런 내용을 담습니다.&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;GET, POST 같은 HTTP 메서드는 무엇인지&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;FastAPI는 이 정보를 코드에서 읽어 자동으로 문서를 만듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래와 같은 코드가 있다고 해보겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;main.py&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from fastapi import FastAPI

app = FastAPI()


@app.get(&quot;/hello&quot;)
def read_hello():
    return {&quot;message&quot;: &quot;Hello FastAPI&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;여기서 FastAPI는 다음 정보를 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;@app.get(&quot;/hello&quot;)&lt;/code&gt;를 보고 &lt;code&gt;/hello&lt;/code&gt;라는 GET API가 있다는 것을 압니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;read_hello()&lt;/code&gt; 함수가 이 API를 처리한다는 것도 압니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 반환값을 통해 이 API가 어떤 응답을 줄 수 있는지 문서에 표시할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 단순한 예제에서는 정보가 많지 않습니다. 하지만 타입 힌트와 Pydantic 모델을 함께 사용하면 문서가 훨씬 구체적으로 만들어집니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Swagger UI는 어디에서 확인할 수 있을까&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI 앱을 실행하면 기본적으로 아래 주소에서 Swagger UI를 볼 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://127.0.0.1:8000/docs&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 서버를 실행하는 명령어는 보통 아래처럼 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;fastapi dev main.py&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;또는 Uvicorn을 직접 사용한다면 이렇게 실행할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;uvicorn main:app --reload&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;code&gt;/docs&lt;/code&gt;에 접속하면 API 목록이 자동으로 정리된 화면이 나옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 끝이 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swagger UI는 단순히 읽기만 하는 문서가 아닙니다. 화면에서 바로 API 요청을 보내고 응답을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입문자 입장에서는 Postman 같은 별도 도구 없이도 API를 테스트할 수 있어서 꽤 편합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;FastAPI가 문서를 자동으로 만드는 원리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI의 자동 문서 생성은 크게 세 가지 요소를 바탕으로 동작합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째는 &lt;b&gt;라우터 정보&lt;/b&gt;입니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;@app.get(&quot;/items&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;이 코드를 보면 FastAPI는 &lt;code&gt;/items&lt;/code&gt;라는 주소와 GET 메서드를 문서에 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째는 &lt;b&gt;타입 힌트&lt;/b&gt;입니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;@app.get(&quot;/items/{item_id}&quot;)
def read_item(item_id: int):
    return {&quot;item_id&quot;: item_id}&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;code&gt;item_id: int&lt;/code&gt;라고 적으면 FastAPI는 &lt;code&gt;item_id&lt;/code&gt;가 정수여야 한다는 것을 압니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Swagger UI 문서에도 &lt;code&gt;item_id&lt;/code&gt;가 integer 타입으로 표시됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 번째는 &lt;b&gt;Pydantic 모델&lt;/b&gt;입니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;from pydantic import BaseModel


class Item(BaseModel):
    name: str
    price: int&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;이런 모델을 요청 본문에 사용하면, Swagger UI는 &lt;code&gt;name&lt;/code&gt;과 &lt;code&gt;price&lt;/code&gt;가 필요한 값이라는 것을 문서에 보여줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, FastAPI 문서는 별도의 문서 작성 도구가 아니라 &lt;b&gt;코드 구조를 바탕으로 자동 생성되는 API 명세&lt;/b&gt;에 가깝습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;직접 확인해보는 간단한 예제&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제를 &lt;code&gt;main.py&lt;/code&gt; 파일에 작성해보겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;main.py&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()


class ItemCreate(BaseModel):
    name: str
    price: int
    description: str | None = None


@app.get(&quot;/&quot;)
def read_root():
    return {&quot;message&quot;: &quot;FastAPI 문서 자동 생성 예제입니다.&quot;}


@app.get(&quot;/items/{item_id}&quot;)
def read_item(item_id: int):
    return {
        &quot;item_id&quot;: item_id,
        &quot;name&quot;: &quot;keyboard&quot;,
        &quot;price&quot;: 30000,
    }


@app.post(&quot;/items&quot;)
def create_item(item: ItemCreate):
    return {
        &quot;message&quot;: &quot;상품이 생성되었습니다.&quot;,
        &quot;item&quot;: item,
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버를 실행합니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;fastapi dev main.py&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저에서 아래 주소로 접속합니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://127.0.0.1:8000/docs&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;그러면 Swagger UI 화면에 API가 세 개 보입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;GET /&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /items/{item_id}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;POST /items&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 부분은 &lt;code&gt;POST /items&lt;/code&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;ItemCreate&lt;/code&gt; 모델에 작성한 &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;price&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt; 필드가 Swagger UI에 자동으로 표시됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드에 타입을 잘 적어두면 문서도 그만큼 정확해집니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;요청값과 응답값이 문서에 표시되는 방식&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI에서 문서 품질을 높이려면 타입 힌트를 대충 쓰면 안 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래 코드는 문서 입장에서 정보가 부족합니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;@app.post(&quot;/items&quot;)
def create_item(item):
    return item&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;code&gt;item&lt;/code&gt;이 어떤 구조인지 알 수 없기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 아래처럼 Pydantic 모델을 사용하면 문서가 훨씬 명확해집니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;from pydantic import BaseModel


class ItemCreate(BaseModel):
    name: str
    price: int
    description: str | None = None&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;이 모델을 API에 연결하면 FastAPI는 다음 내용을 문서에 반영합니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;필드&lt;/th&gt;
&lt;th&gt;타입&lt;/th&gt;
&lt;th&gt;필수 여부&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;name&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;필수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;price&lt;/td&gt;
&lt;td&gt;integer&lt;/td&gt;
&lt;td&gt;필수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;description&lt;/td&gt;
&lt;td&gt;string 또는 null&lt;/td&gt;
&lt;td&gt;선택&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;description: str | None = None&lt;/code&gt;은 초보자가 자주 헷갈리는 부분입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;str | None&lt;/code&gt;은 &amp;ldquo;문자열이거나 None일 수 있다&amp;rdquo;는 뜻입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뒤의 &lt;code&gt;= None&lt;/code&gt;은 &amp;ldquo;값을 보내지 않아도 된다&amp;rdquo;는 기본값입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 이 필드는 선택값으로 문서에 표시됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;code&gt;Field()&lt;/code&gt;로 문서 설명과 예시 추가하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동 생성된 문서를 조금 더 친절하게 만들고 싶다면 Pydantic의 &lt;code&gt;Field()&lt;/code&gt;를 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입만 적으면 Swagger UI에 필드 이름과 타입 중심으로 표시됩니다. 여기에 &lt;code&gt;description&lt;/code&gt;이나 &lt;code&gt;examples&lt;/code&gt;를 넣으면 문서 화면에서 해당 필드가 어떤 값인지 더 쉽게 이해할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;main.py&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;from fastapi import FastAPI
from pydantic import BaseModel, Field

app = FastAPI()


class ItemCreate(BaseModel):
    name: str = Field(
        description=&quot;상품의 이름입니다.&quot;,
        examples=[&quot;무선 마우스&quot;],
    )
    price: int = Field(
        description=&quot;상품의 가격(원)입니다.&quot;,
        examples=[15000],
    )
    description: str | None = Field(
        default=None,
        description=&quot;상품 설명입니다.&quot;,
        examples=[&quot;블루투스 연결을 지원하는 무선 마우스&quot;],
    )


@app.post(&quot;/items&quot;)
def create_item(item: ItemCreate):
    return {
        &quot;message&quot;: &quot;상품이 생성되었습니다.&quot;,
        &quot;item&quot;: item,
    }&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;이렇게 작성하면 Swagger UI에서 각 필드의 설명과 예시를 함께 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;default=None&lt;/code&gt;은 이 필드가 선택값이라는 뜻입니다. 앞에서 봤던 &lt;code&gt;description: str | None = None&lt;/code&gt;과 같은 역할을 하지만, &lt;code&gt;Field()&lt;/code&gt;를 사용할 때는 기본값과 설명을 한곳에 묶어 적을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로는 이 차이가 협업할 때 꽤 큽니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드 개발자나 API를 사용하는 사람이 문서를 봤을 때 &amp;ldquo;이 값에 뭘 넣어야 하지?&amp;rdquo;라고 덜 헷갈리기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 처음부터 모든 필드에 설명을 길게 붙일 필요는 없습니다. 외부에 공유되는 API이거나, 이름만 보고 의미를 알기 어려운 필드부터 설명을 추가하는 정도면 충분합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿼리 파라미터나 경로 파라미터 설명을 더 자세히 넣고 싶다면 FastAPI의 &lt;code&gt;Query()&lt;/code&gt;, &lt;code&gt;Path()&lt;/code&gt;를 사용할 수도 있습니다. 이 글에서는 Swagger 문서 자동 생성 흐름이 핵심이므로, 여기서는 Pydantic 모델의 &lt;code&gt;Field()&lt;/code&gt; 정도만 알아두면 됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Swagger UI에서 API를 테스트하는 방법&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swagger UI의 장점은 문서 화면에서 바로 API를 실행해볼 수 있다는 점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &lt;code&gt;POST /items&lt;/code&gt;를 테스트하려면 아래 순서로 진행하면 됩니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;/docs&lt;/code&gt; 화면에서 &lt;code&gt;POST /items&lt;/code&gt;를 클릭합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Try it out&lt;/code&gt; 버튼을 누릅니다.&lt;/li&gt;
&lt;li&gt;요청 본문에 JSON 값을 입력합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Execute&lt;/code&gt; 버튼을 누릅니다.&lt;/li&gt;
&lt;li&gt;응답 코드와 응답 본문을 확인합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시 요청값은 아래처럼 넣을 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;mouse&quot;,
  &quot;price&quot;: 15000,
  &quot;description&quot;: &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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 API 입문자에게 특히 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 작성하고, 브라우저에서 바로 테스트하고, 응답까지 확인할 수 있기 때문입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;code&gt;/openapi.json&lt;/code&gt;은 무엇일까&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swagger UI 뒤에는 OpenAPI 스키마가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI는 기본적으로 아래 주소에서 OpenAPI JSON을 제공합니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://127.0.0.1:8000/openapi.json&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;이 파일에는 API 구조가 JSON 형태로 정리되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swagger UI는 이 JSON 정보를 읽어서 사람이 보기 좋은 문서 화면으로 보여줍니다.&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI 코드&lt;br /&gt;&amp;rarr; OpenAPI 스키마 생성&lt;br /&gt;&amp;rarr; &lt;code&gt;/openapi.json&lt;/code&gt; 제공&lt;br /&gt;&amp;rarr; Swagger UI가 이 정보를 읽어서 &lt;code&gt;/docs&lt;/code&gt; 화면 구성&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Swagger UI는 문서 화면이고, OpenAPI는 문서의 원본 데이터에 가깝다고 보면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI 공식 문서에서도 기본 OpenAPI 스키마 URL이 &lt;code&gt;/openapi.json&lt;/code&gt;이라고 안내합니다. 이 URL은 &lt;code&gt;openapi_url&lt;/code&gt; 설정으로 바꿀 수 있고, &lt;code&gt;openapi_url=None&lt;/code&gt;으로 설정하면 OpenAPI 스키마와 이를 사용하는 문서 UI가 비활성화됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;ReDoc과 Swagger UI의 차이&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI는 Swagger UI뿐 아니라 ReDoc도 기본으로 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ReDoc은 아래 주소에서 확인할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://127.0.0.1:8000/redoc&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;둘 다 API 문서를 보여주지만 느낌이 조금 다릅니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;Swagger UI&lt;/th&gt;
&lt;th&gt;ReDoc&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;기본 주소&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/docs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/redoc&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;특징&lt;/td&gt;
&lt;td&gt;API를 직접 실행해보기 좋음&lt;/td&gt;
&lt;td&gt;문서를 읽기 좋게 정리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;용도&lt;/td&gt;
&lt;td&gt;개발 중 테스트&lt;/td&gt;
&lt;td&gt;외부 공유용 문서 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;입문자 추천&lt;/td&gt;
&lt;td&gt;먼저 볼 만함&lt;/td&gt;
&lt;td&gt;구조 파악용으로 좋음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발하면서 API를 직접 눌러보고 테스트하려면 Swagger UI가 편합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 전체 API 구조를 문서처럼 읽고 싶다면 ReDoc이 더 깔끔하게 느껴질 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;문서 제목과 설명 추가하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 문서도 충분히 쓸 수 있지만, 프로젝트 이름과 설명을 넣으면 더 보기 좋아집니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;main.py&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;from fastapi import FastAPI

app = FastAPI(
    title=&quot;상품 관리 API&quot;,
    description=&quot;상품 등록, 조회 기능을 제공하는 FastAPI 예제입니다.&quot;,
    version=&quot;1.0.0&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;이렇게 작성하면 Swagger UI 상단에 API 제목, 설명, 버전 정보가 표시됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은 차이지만 협업할 때는 꽤 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API 문서를 보는 사람이 이 서버가 어떤 역할을 하는지 바로 이해할 수 있기 때문입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;문서 주소를 바꾸는 방법&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 Swagger UI 주소는 &lt;code&gt;/docs&lt;/code&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 프로젝트에 따라 문서 주소를 바꾸고 싶을 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;main.py&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;from fastapi import FastAPI

app = FastAPI(
    docs_url=&quot;/api-docs&quot;,
    redoc_url=&quot;/api-redoc&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;이렇게 설정하면 Swagger UI는 아래 주소에서 열립니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://127.0.0.1:8000/api-docs&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;ReDoc은 아래 주소에서 열립니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://127.0.0.1:8000/api-redoc&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;FastAPI 공식 문서에서도 Swagger UI 주소는 &lt;code&gt;docs_url&lt;/code&gt;, ReDoc 주소는 &lt;code&gt;redoc_url&lt;/code&gt;로 설정할 수 있다고 안내합니다. 각각 &lt;code&gt;None&lt;/code&gt;으로 설정하면 해당 문서 UI를 끌 수도 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;운영 환경에서는 문서를 그대로 열어둬도 될까&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 중에는 &lt;code&gt;/docs&lt;/code&gt;가 매우 편합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 운영 환경에서는 한 번 더 생각해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API 문서가 열려 있으면 외부 사용자가 서버의 엔드포인트 구조를 쉽게 볼 수 있습니다. 물론 문서가 열려 있다고 해서 바로 보안 문제가 생기는 것은 아닙니다. 인증과 권한 처리가 제대로 되어 있다면 실제 데이터 접근은 막을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 내부 관리자 API, 테스트용 API, 아직 공개하면 안 되는 API가 문서에 노출될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 운영 환경에서는 보통 아래 중 하나를 선택합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문서 UI를 비활성화한다.&lt;/li&gt;
&lt;li&gt;관리자나 내부망에서만 접근 가능하게 제한한다.&lt;/li&gt;
&lt;li&gt;공개 가능한 API와 내부 API를 분리한다.&lt;/li&gt;
&lt;li&gt;인증이 필요한 API는 보안 스키마를 명확히 설정한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서를 끄고 싶다면 이렇게 작성할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;main.py&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;from fastapi import FastAPI

app = FastAPI(
    docs_url=None,
    redoc_url=None,
)&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;OpenAPI 스키마 자체까지 끄고 싶다면 아래처럼 설정할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;main.py&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;nix&quot;&gt;&lt;code&gt;from fastapi import FastAPI

app = FastAPI(
    openapi_url=None,
)&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;code&gt;/openapi.json&lt;/code&gt;이 비활성화되고, 이를 기반으로 동작하는 Swagger UI와 ReDoc도 함께 비활성화됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;문서 자동 생성이 편하려면 코드도 명확해야 한다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI의 Swagger 문서 자동 생성은 코드를 대신 설명해주는 기능입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 코드 자체가 애매하면 문서도 애매해집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 함수 이름, 모델 이름, 필드 이름을 대충 적으면 문서에서도 그대로 보입니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class Data(BaseModel):
    a: str
    b: int&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;code&gt;a&lt;/code&gt;와 &lt;code&gt;b&lt;/code&gt;가 무엇인지 알기 어렵습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 더 명확하게 작성하는 편이 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class ProductCreate(BaseModel):
    name: str
    price: int&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;API 함수도 마찬가지입니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;@app.post(&quot;/products&quot;)
def create_product(product: ProductCreate):
    return product&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;ldquo;상품을 생성하는 API&amp;rdquo;라는 느낌이 훨씬 분명해집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI 문서 자동 생성 기능을 잘 쓰려면 결국 코드의 이름을 잘 짓는 습관이 중요합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;입문자가 자주 헷갈리는 부분&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;code&gt;/docs&lt;/code&gt;가 실제 API 주소는 아니다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;/docs&lt;/code&gt;는 API 테스트와 문서 확인을 위한 화면입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 API 주소는 &lt;code&gt;/items&lt;/code&gt;, &lt;code&gt;/products&lt;/code&gt;, &lt;code&gt;/users&lt;/code&gt;처럼 직접 만든 엔드포인트입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Swagger UI가 데이터베이스를 만들어주는 것은 아니다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swagger UI는 API를 보여주고 테스트하는 화면입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 저장, DB 연결, 로그인 처리 같은 기능은 직접 구현해야 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;타입 힌트를 적으면 검증과 문서화가 함께 좋아진다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI에서는 타입 힌트가 단순한 메모가 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청값 검증에도 쓰이고, 문서 자동 생성에도 반영됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 FastAPI를 배울 때는 타입 힌트를 함께 익히는 편이 좋습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;마무리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI의 Swagger 문서 자동 생성은 단순한 편의 기능이 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API를 만들고, 테스트하고, 다른 사람에게 설명하는 흐름을 하나로 이어주는 기능에 가깝습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입문 단계에서는 &lt;code&gt;/docs&lt;/code&gt;에 접속해서 직접 요청을 보내보는 것만으로도 FastAPI 구조를 훨씬 빨리 이해할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 기준은 하나입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문서가 잘 나오게 하려면 코드도 명확해야 합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경로 이름, 함수 이름, 타입 힌트, Pydantic 모델을 신경 써서 작성하면 Swagger UI 문서도 자연스럽게 좋아집니다. FastAPI를 처음 배운다면 API 기능을 하나 만들 때마다 &lt;code&gt;/docs&lt;/code&gt;에서 어떻게 보이는지 같이 확인해보는 습관을 들이는 게 좋습니다.&lt;/p&gt;</description>
      <category>개발/FastAPI</category>
      <category>AI코딩</category>
      <category>Python</category>
      <category>개발도구</category>
      <category>코딩입문</category>
      <category>파이썬</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/49</guid>
      <comments>https://notebase.tistory.com/entry/fastapi-swagger-auto-docs#entry49comment</comments>
      <pubDate>Tue, 26 May 2026 14:49:57 +0900</pubDate>
    </item>
    <item>
      <title>FastAPI Pydantic 모델 기초: 요청 데이터를 검증하는 방법</title>
      <link>https://notebase.tistory.com/entry/fastapi-pydantic-model-basics</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI에서 Pydantic 모델을 사용하는 이유와 BaseModel 작성법, POST 요청 데이터 검증, Swagger UI 문서 자동 생성 흐름을 초보자 기준으로 정리합니다.&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;FastAPI Pydantic 모델은 API로 들어오는 JSON 데이터를 정해진 구조로 받고, 타입이 맞는지 자동으로 검증할 때 사용한다. &lt;code&gt;POST&lt;/code&gt; 요청을 다루기 시작했다면 거의 반드시 만나게 되는 개념이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI를 처음 배울 때는 보통 이런 코드부터 시작한다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;@app.get(&quot;/items/{item_id}&quot;)
def read_item(item_id: int):
    return {&quot;item_id&quot;: item_id}&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;이 단계에서는 URL에 들어온 값을 함수 인자로 받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 회원가입, 게시글 작성, 상품 등록처럼 여러 값을 한 번에 보내야 하는 상황은 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 클라이언트가 이런 데이터를 보낸다고 해보자.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;맥북 파우치&quot;,
  &quot;price&quot;: 25000,
  &quot;is_active&quot;: true
}&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;이 데이터는 URL 경로에 넣기 어렵다. 보통 HTTP 요청의 &lt;b&gt;본문, 즉 Request Body&lt;/b&gt;에 담아서 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI 공식 문서에서도 요청 본문을 선언할 때 Pydantic 모델을 사용한다고 설명한다. FastAPI는 이 모델을 기반으로 데이터 검증과 문서화를 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고: [FastAPI 공식 문서 - Request Body](https://fastapi.tiangolo.com/tutorial/body/)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Pydantic 모델은 무엇인가&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pydantic은 파이썬 타입 힌트를 이용해 데이터 구조를 정의하고, 들어온 데이터가 그 구조에 맞는지 검증하는 라이브러리다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글의 예제는 &lt;b&gt;2026년 5월 기준 FastAPI와 Pydantic v2 문법&lt;/b&gt;을 기준으로 작성했다. 오래된 튜토리얼에서 보이는 Pydantic v1 문법이나 에러 메시지 형태와 일부 차이가 있을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI에서는 Pydantic을 API 요청과 응답 처리에 적극적으로 활용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 보면 Pydantic 모델은 이런 역할을 한다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;클라이언트가 보낸 JSON
        &amp;darr;
Pydantic 모델로 구조 확인
        &amp;darr;
타입이 맞으면 FastAPI 함수로 전달
        &amp;darr;
타입이 틀리면 자동으로 에러 응답&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;code&gt;if price &amp;lt; 0&lt;/code&gt;, &lt;code&gt;if name is None&lt;/code&gt; 같은 코드를 매번 작성하지 않아도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점은 Pydantic 모델이 단순히 데이터를 담는 그릇이 아니라는 것이다. API가 받을 수 있는 데이터의 모양을 정하고, 잘못된 요청을 함수 실행 전에 걸러내는 기준이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고: [Pydantic 공식 문서 - Models](https://pydantic.dev/docs/validation/latest/concepts/models/)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;BaseModel로 요청 데이터 구조 만들기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pydantic 모델은 보통 &lt;code&gt;BaseModel&lt;/code&gt;을 상속해서 만든다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;from pydantic import BaseModel

class Item(BaseModel):
    name: str
    price: int
    is_active: bool&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;code&gt;Item&lt;/code&gt;이라는 데이터 구조를 만든 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 필드는 다음 의미를 가진다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;필드&lt;/th&gt;
&lt;th&gt;타입&lt;/th&gt;
&lt;th&gt;의미&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;문자열&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;price&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;int&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;정수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;is_active&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bool&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;참/거짓 값&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점은 이 모델이 실제로 &lt;b&gt;검증 규칙&lt;/b&gt;으로도 사용된다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &lt;code&gt;price&lt;/code&gt;에 문자열이 들어오거나, 필수 값인 &lt;code&gt;name&lt;/code&gt;이 빠지면 FastAPI가 요청을 정상 처리하지 않고 검증 에러를 반환한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;POST 요청에서 Pydantic 모델 사용하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 FastAPI 코드에 연결해보자.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    name: str
    price: int
    is_active: bool

@app.post(&quot;/items&quot;)
def create_item(item: Item):
    return item&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 이 부분이다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;def create_item(item: Item):&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;code&gt;item&lt;/code&gt;의 타입을 &lt;code&gt;Item&lt;/code&gt;으로 지정했기 때문에 FastAPI는 요청 본문을 &lt;code&gt;Item&lt;/code&gt; 모델에 맞춰 해석한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트가 아래처럼 요청을 보내면 정상 처리된다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;맥북 파우치&quot;,
  &quot;price&quot;: 25000,
  &quot;is_active&quot;: true
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응답은 대략 이렇게 나온다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;맥북 파우치&quot;,
  &quot;price&quot;: 25000,
  &quot;is_active&quot;: true
}&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;실제로는 이 데이터를 데이터베이스에 저장하거나, 추가 로직을 거친 뒤 응답하게 된다. 지금은 Pydantic 모델 흐름을 보기 위해 그대로 반환하는 예시다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Swagger UI에서도 자동으로 보인다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI의 편한 점은 Pydantic 모델을 작성하면 Swagger UI 문서에도 요청 본문 구조가 자동으로 반영된다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버를 실행한 뒤 아래 주소로 들어가면 된다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://127.0.0.1:8000/docs&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;code&gt;POST /items&lt;/code&gt;를 열어보면 &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;price&lt;/code&gt;, &lt;code&gt;is_active&lt;/code&gt; 필드가 포함된 요청 예시가 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분이 입문자에게 꽤 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pydantic 모델을 작성하는 것은 단순히 파이썬 코드 내부에서만 의미가 있는 게 아니다. API를 사용하는 사람에게 &amp;ldquo;이 API는 이런 JSON을 받는다&amp;rdquo;는 문서 역할도 같이 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;타입 검증은 어떻게 동작할까&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음처럼 잘못된 요청을 보낸다고 해보자.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;맥북 파우치&quot;,
  &quot;price&quot;: &quot;비쌈&quot;,
  &quot;is_active&quot;: true
}&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;code&gt;price&lt;/code&gt;는 &lt;code&gt;int&lt;/code&gt;로 선언했는데 문자열 &lt;code&gt;&quot;비쌈&quot;&lt;/code&gt;이 들어왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 FastAPI는 함수를 실행하기 전에 요청을 막고 검증 에러를 반환한다. 개발자가 직접 예외 처리를 작성하지 않아도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 이런 식의 응답을 볼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;detail&quot;: [
    {
      &quot;type&quot;: &quot;int_parsing&quot;,
      &quot;loc&quot;: [&quot;body&quot;, &quot;price&quot;],
      &quot;msg&quot;: &quot;Input should be a valid integer&quot;,
      &quot;input&quot;: &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;/p&gt;
&lt;pre class=&quot;d&quot;&gt;&lt;code&gt;body 안의 price 값이 int로 변환될 수 없다&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;실제로는 이런 검증 실패 응답이 디버깅에 도움이 된다. 어느 위치의 어떤 값이 문제인지 FastAPI가 비교적 구체적으로 알려주기 때문이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;기본값 넣기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 값을 매번 클라이언트가 보내야 하는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본값을 지정할 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;from pydantic import BaseModel

class Item(BaseModel):
    name: str
    price: int
    is_active: bool = True&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;code&gt;is_active&lt;/code&gt;를 보내지 않아도 된다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;맥북 파우치&quot;,
  &quot;price&quot;: 25000
}&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;FastAPI는 &lt;code&gt;is_active&lt;/code&gt; 값을 자동으로 &lt;code&gt;True&lt;/code&gt;로 채운다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 기본값은 게시글의 공개 여부, 상품 활성화 여부, 알림 수신 여부처럼 기본 상태가 정해져 있는 필드에 자주 사용한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;선택값 처리하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값이 없을 수도 있는 필드는 &lt;code&gt;None&lt;/code&gt;을 허용하도록 작성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python 3.10 이상에서는 아래처럼 쓴다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;from pydantic import BaseModel

class Item(BaseModel):
    name: str
    price: int
    description: str | None = None&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;Python 3.9 이하 버전까지 고려한다면 &lt;code&gt;Optional&lt;/code&gt;을 사용한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;from typing import Optional
from pydantic import BaseModel

class Item(BaseModel):
    name: str
    price: int
    description: Optional[str] = None&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 예시는 의미가 거의 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 &lt;code&gt;str | None = None&lt;/code&gt;은 두 부분으로 나눠서 보는 게 좋다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;코드&lt;/th&gt;
&lt;th&gt;의미&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;`str \&lt;/td&gt;
&lt;td&gt;None`&lt;/td&gt;
&lt;td&gt;이 필드에는 문자열이 들어올 수도 있고, &lt;code&gt;None&lt;/code&gt;이 들어올 수도 있다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;= None&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;클라이언트가 이 필드를 아예 보내지 않으면 기본값으로 &lt;code&gt;None&lt;/code&gt;을 넣는다&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 &lt;code&gt;str | None&lt;/code&gt;은 &lt;b&gt;값의 타입&lt;/b&gt;을 정하는 부분이고, &lt;code&gt;= None&lt;/code&gt;은 &lt;b&gt;필드를 생략했을 때의 기본값&lt;/b&gt;을 정하는 부분이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보자가 여기서 자주 헷갈리는 코드가 있다.&lt;/p&gt;
&lt;pre class=&quot;rust&quot;&gt;&lt;code&gt;description: str | None&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;code&gt;None&lt;/code&gt;은 허용하지만, 필드 자체는 요청에 포함되어야 하는 것으로 해석될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pydantic v2에서는 &amp;ldquo;필드가 필수인지 아닌지&amp;rdquo;를 판단할 때 기본값 유무가 중요하다. 그래서 실제로 생략 가능한 필드라면 아래처럼 기본값까지 같이 지정하는 편이 안전하다.&lt;/p&gt;
&lt;pre class=&quot;rust&quot;&gt;&lt;code&gt;description: str | None = None&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;ldquo;안 보내도 되는 선택 필드&amp;rdquo;라는 의미가 더 분명해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고: [Pydantic 공식 문서 - Migration Guide](https://pydantic.dev/docs/validation/latest/get-started/migration/)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Field로 더 구체적인 조건 넣기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입만으로는 부족한 경우가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 가격은 정수여야 할 뿐 아니라 0보다 커야 한다. 상품명은 너무 짧으면 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴 때 &lt;code&gt;Field&lt;/code&gt;를 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;from pydantic import BaseModel, Field

class Item(BaseModel):
    name: str = Field(min_length=2, max_length=50)
    price: int = Field(gt=0)
    description: str | None = Field(default=None, max_length=300)&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;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;코드&lt;/th&gt;
&lt;th&gt;의미&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;min_length=2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;최소 2글자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;max_length=50&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;최대 50글자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gt=0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0보다 커야 함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;default=None&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;기본값은 None&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;FastAPI 공식 문서에서도 Pydantic의 &lt;code&gt;Field&lt;/code&gt;를 사용해 모델 속성에 추가 검증과 메타데이터를 선언할 수 있다고 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고: [FastAPI 공식 문서 - Body Fields](https://fastapi.tiangolo.com/tutorial/body-fields/)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;gt&lt;/code&gt;는 greater than, 즉 &amp;ldquo;보다 큼&amp;rdquo;이라는 뜻이다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;price: int = Field(gt=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;code&gt;price&lt;/code&gt;가 0보다 큰 정수여야 한다는 의미다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 아래 요청은 실패한다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;가방&quot;,
  &quot;price&quot;: 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;가격이 0보다 커야 하는데 0이 들어왔기 때문이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;요청 모델과 응답 모델은 분리하는 게 좋다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보 단계에서는 요청으로 받은 모델을 그대로 응답해도 된다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;@app.post(&quot;/items&quot;)
def create_item(item: Item):
    return item&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제 서비스에서는 요청 모델과 응답 모델을 분리하는 경우가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 상품을 만들 때 클라이언트는 &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;price&lt;/code&gt;만 보낸다. 그런데 응답에는 서버가 만든 &lt;code&gt;id&lt;/code&gt;도 포함해야 한다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from fastapi import FastAPI
from pydantic import BaseModel, Field

app = FastAPI()

class ItemCreate(BaseModel):
    name: str = Field(min_length=2, max_length=50)
    price: int = Field(gt=0)
    description: str | None = None

class ItemResponse(BaseModel):
    id: int
    name: str
    price: int
    description: str | None = None

@app.post(&quot;/items&quot;, response_model=ItemResponse)
def create_item(item: ItemCreate):
    return {
        &quot;id&quot;: 1,
        &quot;name&quot;: item.name,
        &quot;price&quot;: item.price,
        &quot;description&quot;: item.description
    }&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;code&gt;ItemCreate&lt;/code&gt;는 요청용 모델이고, &lt;code&gt;ItemResponse&lt;/code&gt;는 응답용 모델이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI의 &lt;code&gt;response_model&lt;/code&gt;은 응답 데이터 문서화, 검증, 변환, 필터링 등에 사용된다. 공식 문서에서도 &lt;code&gt;response_model&lt;/code&gt;을 통해 응답 데이터 구조를 선언할 수 있다고 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고: [FastAPI 공식 문서 - Response Model](https://fastapi.tiangolo.com/tutorial/response-model/)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 FastAPI에서는 함수의 반환 타입 힌트로 응답 모델을 표현할 수도 있다. ```python @app.post(&quot;/items&quot;) def create_item(item: ItemCreate) -&amp;gt; ItemResponse: pass &lt;code&gt;`&lt;/code&gt; 위 코드는 반환 타입 힌트 문법을 보여주기 위한 간단한 예시다. 실제 코드에서는 &lt;code&gt;pass&lt;/code&gt; 대신 데이터를 저장하거나, 응답으로 보낼 값을 만들어 &lt;code&gt;return&lt;/code&gt;해야 한다. 다만 입문 단계에서는 &lt;code&gt;response_model=ItemResponse&lt;/code&gt;처럼 명시적으로 적는 방식이 요청 모델과 응답 모델의 차이를 이해하기 더 쉽다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점은 보안이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 회원가입 API에서 요청 모델에 &lt;code&gt;password&lt;/code&gt;가 있다고 해서 응답에도 그대로 비밀번호를 보내면 안 된다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class UserCreate(BaseModel):
    email: str
    password: str

class UserResponse(BaseModel):
    id: int
    email: str&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;자주 헷갈리는 부분&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. Pydantic 모델은 데이터베이스 모델이 아니다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pydantic 모델은 요청과 응답 데이터의 구조를 검증하는 데 주로 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스 테이블을 정의하는 모델과는 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 SQLAlchemy 모델은 DB 테이블과 연결되고, Pydantic 모델은 API 입출력 데이터와 연결된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 둘 다 &amp;ldquo;모델&amp;rdquo;이라고 부르기 때문에 헷갈릴 수 있다.&lt;/p&gt;
&lt;pre class=&quot;ldif&quot;&gt;&lt;code&gt;Pydantic 모델: API 요청/응답 데이터 검증
ORM 모델: 데이터베이스 테이블 매핑&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 타입 힌트만 쓴다고 자동 검증되는 것은 아니다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 함수에서 이렇게 쓴다고 해서 일반 파이썬 코드가 자동으로 타입을 막아주는 것은 아니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;def create_item(price: int):
    return price&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;하지만 FastAPI와 Pydantic을 함께 쓰면 이 타입 정보가 실제 요청 검증에 활용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 차이를 이해해야 한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. GET 요청에는 보통 Body를 쓰지 않는다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI는 복잡한 경우 GET 요청에 Body를 받는 것도 지원하지만, 일반적인 API 설계에서는 &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, &lt;code&gt;PATCH&lt;/code&gt; 요청에서 Body를 사용하는 흐름이 더 자연스럽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI 공식 문서도 데이터를 보낼 때는 보통 &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;, &lt;code&gt;PATCH&lt;/code&gt; 등을 사용한다고 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고: [FastAPI 공식 문서 - Request Body](https://fastapi.tiangolo.com/tutorial/body/)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조회 조건이 단순하면 &lt;code&gt;GET&lt;/code&gt;의 쿼리 파라미터를 쓰고, 생성이나 수정처럼 데이터 묶음을 보내야 하면 요청 본문을 쓰는 식으로 나누면 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;전체 예제 코드&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드는 지금까지 내용을 합친 간단한 예제다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;from fastapi import FastAPI
from pydantic import BaseModel, Field

app = FastAPI()

class ItemCreate(BaseModel):
    name: str = Field(min_length=2, max_length=50)
    price: int = Field(gt=0)
    description: str | None = Field(default=None, max_length=300)
    is_active: bool = True

class ItemResponse(BaseModel):
    id: int
    name: str
    price: int
    description: str | None = None
    is_active: bool

@app.post(&quot;/items&quot;, response_model=ItemResponse)
def create_item(item: ItemCreate):
    return {
        &quot;id&quot;: 1,
        &quot;name&quot;: item.name,
        &quot;price&quot;: item.price,
        &quot;description&quot;: item.description,
        &quot;is_active&quot;: item.is_active
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 실행은 이전 글에서 사용한 방식과 동일하다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;fastapi dev main.py&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;code&gt;app.py&lt;/code&gt;로 만들었다면 아래처럼 파일명에 맞춰 실행하면 된다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;fastapi dev app.py&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;또는 환경에 따라 Uvicorn을 직접 실행할 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;uvicorn main:app --reload&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;code&gt;main:app&lt;/code&gt;은 &lt;code&gt;main.py&lt;/code&gt; 파일 안의 &lt;code&gt;app = FastAPI()&lt;/code&gt; 객체를 실행한다는 뜻이다. 파일 이름이 &lt;code&gt;app.py&lt;/code&gt;라면 &lt;code&gt;uvicorn app:app --reload&lt;/code&gt;처럼 앞부분도 파일명에 맞춰 바꿔야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저에서 아래 주소로 접속한다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://127.0.0.1:8000/docs&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;code&gt;POST /items&lt;/code&gt;를 열고 &lt;code&gt;Try it out&lt;/code&gt;을 누른 뒤 아래 JSON을 입력해보면 된다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;맥북 파우치&quot;,
  &quot;price&quot;: 25000,
  &quot;description&quot;: &quot;노트북 보호용 파우치&quot;,
  &quot;is_active&quot;: true
}&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;code&gt;price&lt;/code&gt;를 &lt;code&gt;0&lt;/code&gt;으로 바꾸거나, &lt;code&gt;name&lt;/code&gt;을 한 글자로 줄여보면 검증 에러가 어떻게 나오는지도 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로는 정상 요청보다 실패 요청을 일부러 보내보는 게 더 도움이 된다. Pydantic 모델이 어떤 기준으로 요청을 막는지 눈으로 볼 수 있기 때문이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;FastAPI에서 Pydantic 모델을 배울 때의 기준&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI에서 Pydantic 모델은 &amp;ldquo;예쁘게 클래스를 만드는 문법&amp;rdquo;이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API가 받을 수 있는 데이터의 모양을 정하고, 잘못된 요청을 함수 실행 전에 걸러내고, Swagger UI 문서까지 자동으로 정리해주는 기준점에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 아래 네 가지만 구분하면 충분하다.&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;BaseModel: 데이터 구조 정의
타입 힌트: 필드 타입 지정
Field: 세부 검증 조건 추가
response_model: 응답 구조 지정&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;이 흐름이 잡히면 FastAPI의 &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, &lt;code&gt;PATCH&lt;/code&gt; 요청을 훨씬 안정적으로 다룰 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 단계에서는 중첩 모델, 리스트 요청, 여러 모델을 함께 받는 방식까지 확장하면 된다.&lt;/p&gt;</description>
      <category>개발/FastAPI</category>
      <category>AI코딩</category>
      <category>Python</category>
      <category>개발도구</category>
      <category>코딩입문</category>
      <category>파이썬</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/48</guid>
      <comments>https://notebase.tistory.com/entry/fastapi-pydantic-model-basics#entry48comment</comments>
      <pubDate>Tue, 26 May 2026 09:28:36 +0900</pubDate>
    </item>
    <item>
      <title>FastAPI Path Parameter와 Query Parameter 차이 정리</title>
      <link>https://notebase.tistory.com/entry/fastapi-path-query-parameter</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI에서 Path Parameter와 Query Parameter를 어떻게 구분하고 사용하는지 예제 코드와 함께 정리합니다. 필수값, 선택값, 타입 변환, 검증 방식까지 함께 설명합니다.&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;FastAPI Path Parameter와 Query Parameter는 URL로 값을 받는 방식이다. 둘 다 API에서 자주 쓰이지만, 역할은 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI를 처음 배우면 이런 URL을 자주 보게 된다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;/items/1
/items?skip=0&amp;amp;limit=10&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;br /&gt;하지만 &lt;code&gt;/items/1&lt;/code&gt;의 &lt;code&gt;1&lt;/code&gt;은 &lt;b&gt;Path Parameter&lt;/b&gt;이고, &lt;code&gt;?skip=0&amp;amp;limit=10&lt;/code&gt;은 &lt;b&gt;Query Parameter&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 차이를 정확히 알아야 API 주소를 자연스럽게 설계할 수 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Path Parameter와 Query Parameter의 차이&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 기준부터 잡고 가면 편하다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;Path Parameter&lt;/th&gt;
&lt;th&gt;Query Parameter&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;위치&lt;/td&gt;
&lt;td&gt;URL 경로 안&lt;/td&gt;
&lt;td&gt;&lt;code&gt;?&lt;/code&gt; 뒤&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;예시&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/items/1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/items?skip=0&amp;amp;limit=10&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;주 용도&lt;/td&gt;
&lt;td&gt;특정 리소스 지정&lt;/td&gt;
&lt;td&gt;검색, 필터, 정렬, 페이지네이션&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;필수 여부&lt;/td&gt;
&lt;td&gt;보통 필수&lt;/td&gt;
&lt;td&gt;선택값으로 자주 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FastAPI 처리 방식&lt;/td&gt;
&lt;td&gt;경로에 선언된 이름으로 인식&lt;/td&gt;
&lt;td&gt;함수 매개변수 중 경로에 없는 값을 쿼리로 인식&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI에서 Path Parameter는 &lt;code&gt;{item_id}&lt;/code&gt;처럼 중괄호를 사용해 선언한다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from fastapi import FastAPI

app = FastAPI()

@app.get(&quot;/items/{item_id}&quot;)
async def read_item(item_id: int):
    return {&quot;item_id&quot;: item_id}&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;code&gt;/items/{item_id}&lt;/code&gt;의 &lt;code&gt;{item_id}&lt;/code&gt;가 Path Parameter다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;FastAPI Path Parameter 사용법&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Path Parameter는 URL 경로 자체에 값이 들어가는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 상품 상세 조회 API를 만든다고 해보자.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from fastapi import FastAPI

app = FastAPI()

@app.get(&quot;/items/{item_id}&quot;)
async def read_item(item_id: int):
    return {&quot;item_id&quot;: item_id}&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;이 API는 다음 주소로 요청할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;http://127.0.0.1:8000/items/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;그러면 FastAPI는 URL의 &lt;code&gt;1&lt;/code&gt;을 &lt;code&gt;item_id&lt;/code&gt;에 넣어준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응답은 다음과 비슷하다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;item_id&quot;: 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;code&gt;item_id: int&lt;/code&gt;다.&lt;br /&gt;URL로 들어오는 값은 원래 문자열이지만, FastAPI는 타입 힌트를 보고 &lt;code&gt;int&lt;/code&gt;로 변환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 아래처럼 숫자가 아닌 값을 넣으면 어떻게 될까?&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;/items/abc&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;code&gt;item_id&lt;/code&gt;는 &lt;code&gt;int&lt;/code&gt;여야 하는데 &lt;code&gt;abc&lt;/code&gt;는 정수로 바꿀 수 없다.&lt;br /&gt;이 경우 FastAPI는 자동으로 검증 오류를 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 &lt;code&gt;if&lt;/code&gt;문으로 타입 검사를 작성하지 않아도 된다는 점이 FastAPI의 편한 부분이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Path Parameter는 언제 사용할까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Path Parameter는 &lt;b&gt;무엇을 조회할지 URL 경로에서 바로 알 수 있어야 할 때&lt;/b&gt; 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 이런 경우다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;/users/10
/posts/35
/products/7
/orders/202&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;/users/10&lt;/code&gt; &amp;rarr; 10번 사용자&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/posts/35&lt;/code&gt; &amp;rarr; 35번 게시글&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/products/7&lt;/code&gt; &amp;rarr; 7번 상품&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, Path Parameter는 보통 &lt;b&gt;ID나 고유한 이름처럼 리소스를 식별하는 값&lt;/b&gt;에 잘 맞는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;FastAPI Query Parameter 사용법&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Query Parameter는 URL의 &lt;code&gt;?&lt;/code&gt; 뒤에 붙는 값이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 게시글 목록을 조회하면서 검색어를 전달한다고 해보자.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from fastapi import FastAPI

app = FastAPI()

@app.get(&quot;/posts&quot;)
async def read_posts(keyword: str | None = None):
    return {&quot;keyword&quot;: keyword}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청 주소는 이렇게 만들 수 있다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://127.0.0.1:8000/posts?keyword=fastapi&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;code&gt;keyword&lt;/code&gt;에는 &lt;code&gt;&quot;fastapi&quot;&lt;/code&gt;가 들어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응답은 다음과 비슷하다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;keyword&quot;: &quot;fastapi&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;FastAPI에서는 함수 매개변수 중 URL 경로에 선언되지 않은 값이 있으면, 기본적으로 Query Parameter로 인식한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고: 이 글의 예제는 Python 3.10 이상 문법을 기준으로 작성했다. &lt;code&gt;str | None&lt;/code&gt; 문법은 Python 3.10 이상에서 사용할 수 있다. Python 3.9 이하를 사용한다면 &lt;code&gt;from typing import Optional&lt;/code&gt;을 가져온 뒤 &lt;code&gt;Optional[str] = None&lt;/code&gt; 형태로 작성하면 된다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python 3.9 이하에서는 위 코드를 이렇게 바꿔 쓸 수 있다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from typing import Optional

from fastapi import FastAPI

app = FastAPI()

@app.get(&quot;/posts&quot;)
async def read_posts(keyword: Optional[str] = None):
    return {&quot;keyword&quot;: keyword}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Query Parameter는 언제 사용할까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Query Parameter는 &lt;b&gt;목록을 조회하거나 조건을 추가할 때&lt;/b&gt; 자주 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 이런 주소다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;/posts?keyword=fastapi
/posts?category=python
/posts?skip=0&amp;amp;limit=10
/posts?sort=latest&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;keyword=fastapi&lt;/code&gt; &amp;rarr; 검색어&lt;/li&gt;
&lt;li&gt;&lt;code&gt;category=python&lt;/code&gt; &amp;rarr; 카테고리 필터&lt;/li&gt;
&lt;li&gt;&lt;code&gt;skip=0&amp;amp;limit=10&lt;/code&gt; &amp;rarr; 페이지네이션&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sort=latest&lt;/code&gt; &amp;rarr; 정렬 기준&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 값들은 없어도 기본 목록 조회가 가능하다.&lt;br /&gt;그래서 Query Parameter는 선택값으로 설계하는 경우가 많다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Query Parameter의 선택값과 필수값&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Query Parameter는 기본값을 주면 선택값이 된다.&lt;/p&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;@app.get(&quot;/posts&quot;)
async def read_posts(keyword: str | None = None):
    return {&quot;keyword&quot;: keyword}&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;code&gt;keyword&lt;/code&gt;의 기본값은 &lt;code&gt;None&lt;/code&gt;이다.&lt;br /&gt;그래서 아래 주소도 정상적으로 동작한다.&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;/posts&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;code&gt;keyword&lt;/code&gt;에는 &lt;code&gt;None&lt;/code&gt;이 들어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 기본값을 주지 않으면 필수 Query Parameter가 된다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;@app.get(&quot;/posts/search&quot;)
async def search_posts(keyword: str):
    return {&quot;keyword&quot;: keyword}&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;이 API는 반드시 다음처럼 요청해야 한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;/posts/search?keyword=fastapi&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Path Parameter와 Query Parameter 함께 사용하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 API에서는 둘을 같이 쓰는 경우가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 특정 사용자의 게시글 목록을 조회하면서 검색어와 페이지 크기를 함께 받는 API를 만들어보자.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;from fastapi import FastAPI

app = FastAPI()

@app.get(&quot;/users/{user_id}/posts&quot;)
async def read_user_posts(
    user_id: int,
    keyword: str | None = None,
    limit: int = 10
):
    return {
        &quot;user_id&quot;: user_id,
        &quot;keyword&quot;: keyword,
        &quot;limit&quot;: limit
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청은 이렇게 보낼 수 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;/users/3/posts?keyword=fastapi&amp;amp;limit=5&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 값은 다음처럼 들어간다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;user_id&quot;: 3,
  &quot;keyword&quot;: &quot;fastapi&quot;,
  &quot;limit&quot;: 5
}&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;code&gt;user_id&lt;/code&gt;는 Path Parameter다.&lt;br /&gt;&lt;code&gt;keyword&lt;/code&gt;와 &lt;code&gt;limit&lt;/code&gt;은 Query Parameter다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI는 함수 매개변수 순서에 의존해서 Path와 Query를 구분하지 않는다.&lt;br /&gt;경로에 &lt;code&gt;{user_id}&lt;/code&gt;가 있으므로 &lt;code&gt;user_id&lt;/code&gt;는 Path Parameter가 되고, 나머지 값은 Query Parameter로 처리된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Boolean Query Parameter도 자동 변환된다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI는 Query Parameter의 타입도 자동으로 변환한다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;@app.get(&quot;/posts&quot;)
async def read_posts(short: bool = False):
    return {&quot;short&quot;: short}&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;code&gt;short=True&lt;/code&gt;로 처리될 수 있다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;/posts?short=true
/posts?short=True
/posts?short=1
/posts?short=yes
/posts?short=on&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;FastAPI는 타입 힌트를 기준으로 &lt;code&gt;/docs&lt;/code&gt; 문서도 자동 생성한다.&lt;br /&gt;예를 들어 &lt;code&gt;short: bool = False&lt;/code&gt;처럼 선언하면 Swagger UI에서는 해당 값이 boolean 타입으로 표시된다. API를 직접 테스트할 때도 어떤 타입을 기대하는지 바로 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 실제 URL 요청에서는 &lt;code&gt;true&lt;/code&gt;, &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;yes&lt;/code&gt;, &lt;code&gt;on&lt;/code&gt;처럼 여러 표현이 &lt;code&gt;True&lt;/code&gt;로 해석될 수 있다.&lt;br /&gt;팀 내부나 외부 사용자에게 공개하는 API라면 예시 문서에서는 &lt;code&gt;true&lt;/code&gt; / &lt;code&gt;false&lt;/code&gt;처럼 하나의 표기 방식을 정해두는 편이 좋다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;code&gt;Path&lt;/code&gt;와 &lt;code&gt;Query&lt;/code&gt;로 검증 조건 추가하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 타입만 지정하는 것보다 더 자세한 조건을 걸고 싶을 때가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &lt;code&gt;item_id&lt;/code&gt;는 1 이상이어야 하고, 검색어 &lt;code&gt;q&lt;/code&gt;는 최대 50자까지만 허용하고 싶다고 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴 때는 &lt;code&gt;Path&lt;/code&gt;, &lt;code&gt;Query&lt;/code&gt;, &lt;code&gt;Annotated&lt;/code&gt;를 함께 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;from typing import Annotated

from fastapi import FastAPI, Path, Query

app = FastAPI()

@app.get(&quot;/items/{item_id}&quot;)
async def read_item(
    item_id: Annotated[int, Path(ge=1)],
    q: Annotated[str | None, Query(max_length=50)] = None
):
    return {
        &quot;item_id&quot;: item_id,
        &quot;q&quot;: 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;여기서 &lt;code&gt;Path(ge=1)&lt;/code&gt;은 &lt;code&gt;item_id&lt;/code&gt;가 1 이상이어야 한다는 뜻이다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;/items/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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Query(max_length=50)&lt;/code&gt;은 &lt;code&gt;q&lt;/code&gt;의 길이를 최대 50자로 제한한다.&lt;br /&gt;이런 검증 조건은 FastAPI가 자동 생성하는 OpenAPI 문서에도 함께 반영된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자 검증에서는 자주 이런 옵션을 사용한다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;옵션&lt;/th&gt;
&lt;th&gt;의미&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;greater than, 초과&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ge&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;greater than or equal, 이상&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;less than, 미만&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;le&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;less than or equal, 이하&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;code&gt;limit&lt;/code&gt; 값을 1 이상 100 이하로 제한하고 싶다면 이렇게 작성할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from typing import Annotated

from fastapi import Query

async def read_posts(
    limit: Annotated[int, Query(ge=1, le=100)] = 10
):
    return {&quot;limit&quot;: limit}&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;실제로는 목록 API에서 &lt;code&gt;limit&lt;/code&gt;에 이런 제한을 자주 둔다.&lt;br /&gt;제한이 없으면 한 번에 너무 많은 데이터를 요청할 수 있기 때문이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;code&gt;Annotated&lt;/code&gt; 문법은 어떻게 읽으면 될까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입문자 입장에서는 &lt;code&gt;Annotated[int, Path(ge=1)]&lt;/code&gt;가 낯설 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문법은 크게 두 부분으로 보면 된다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Annotated[int, Path(ge=1)]&lt;/code&gt;&lt;/pre&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;code&gt;int&lt;/code&gt; &amp;rarr; 실제 값의 타입&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Path(ge=1)&lt;/code&gt; &amp;rarr; FastAPI에 전달할 검증 조건이나 메타데이터&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 위 코드는 &amp;ldquo;이 값은 &lt;code&gt;int&lt;/code&gt; 타입이고, FastAPI에서는 경로 매개변수로 다루되 1 이상이어야 한다&amp;rdquo;는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Query&lt;/code&gt;도 같은 방식으로 읽으면 된다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Annotated[str | None, Query(max_length=50)]&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;ldquo;문자열이거나 &lt;code&gt;None&lt;/code&gt;이 될 수 있고, 쿼리 파라미터로 받을 때 최대 길이는 50자까지 허용한다&amp;rdquo;는 의미다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 문법이 길어 보일 수 있지만, 타입과 검증 조건을 한곳에 모아둘 수 있어서 코드가 커질수록 관리하기 편하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Path Parameter는 항상 필수다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 자주 헷갈리는 부분이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Query Parameter는 기본값을 &lt;code&gt;None&lt;/code&gt;으로 두면 선택값이 될 수 있다.&lt;br /&gt;하지만 Path Parameter는 기본적으로 선택값이 될 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 이 주소를 보자.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;/items/{item_id}&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;이 API를 호출하려면 &lt;code&gt;item_id&lt;/code&gt;가 반드시 URL에 있어야 한다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;/items/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;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;/items&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;Path Parameter는 URL 경로의 일부다.&lt;br /&gt;그래서 선택적으로 받을 값이라면 Path Parameter가 아니라 Query Parameter로 설계하는 편이 자연스럽다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;실제 API 설계 기준&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘 중 무엇을 써야 할지 헷갈릴 때는 이 기준으로 보면 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;특정 대상을 가리키면 Path Parameter&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;/users/1
/posts/10
/products/3&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;이런 값은 URL에서 빠지면 API의 의미가 바뀐다.&lt;br /&gt;따라서 Path Parameter가 잘 맞는다.&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;@app.get(&quot;/posts/{post_id}&quot;)
async def read_post(post_id: int):
    return {&quot;post_id&quot;: post_id}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;조회 조건을 조절하면 Query Parameter&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;/posts?keyword=fastapi
/posts?limit=10
/posts?sort=latest&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;br /&gt;값이 없어도 기본 조회는 가능하다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;@app.get(&quot;/posts&quot;)
async def read_posts(
    keyword: str | None = None,
    limit: int = 10
):
    return {
        &quot;keyword&quot;: keyword,
        &quot;limit&quot;: limit
    }&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;실제로는 Path와 Query를 같이 쓰는 경우가 많다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;/users/1/posts?limit=10&amp;amp;sort=latest&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;users/1&lt;/code&gt; &amp;rarr; 1번 사용자의&lt;/li&gt;
&lt;li&gt;&lt;code&gt;posts&lt;/code&gt; &amp;rarr; 게시글 목록을&lt;/li&gt;
&lt;li&gt;&lt;code&gt;limit=10&amp;amp;sort=latest&lt;/code&gt; &amp;rarr; 10개씩 최신순으로 조회한다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URL만 봐도 API의 의도가 꽤 명확해진다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;자주 하는 실수&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 검색 조건을 Path Parameter로 만드는 경우&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;/posts/fastapi&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;ldquo;fastapi라는 검색어로 게시글을 찾는다&amp;rdquo;는 의미라면 Query Parameter가 더 자연스럽다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;/posts?keyword=fastapi&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;code&gt;/posts/fastapi&lt;/code&gt;는 보통 &lt;code&gt;fastapi&lt;/code&gt;라는 고유 ID나, 글 제목을 URL에 쓰기 좋게 바꾼 식별자(slug)를 가진 게시글을 조회하는 느낌에 가깝다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. ID를 Query Parameter로만 처리하는 경우&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;/posts?post_id=10&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;동작은 가능하지만, 특정 게시글 하나를 조회하는 API라면 보통 아래가 더 자연스럽다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;/posts/10&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;리소스 식별자는 Path Parameter로 두는 쪽이 REST API 주소 구조에 더 잘 맞는다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 선택값을 Path Parameter로 만들려는 경우&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;/posts/{category}&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;카테고리가 항상 필요한 API라면 괜찮다.&lt;br /&gt;하지만 카테고리 없이 전체 게시글도 조회해야 한다면 Query Parameter가 낫다.&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;/posts
/posts?category=python&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;선택 조건은 Query Parameter로 빼는 편이 API를 확장하기 쉽다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;전체 예제 코드&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 지금까지 본 Path Parameter, Query Parameter, 타입 변환, 검증 조건을 하나로 조합해보자.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;from typing import Annotated

from fastapi import FastAPI, Path, Query

app = FastAPI()

@app.get(&quot;/users/{user_id}/posts&quot;)
async def read_user_posts(
    user_id: Annotated[int, Path(ge=1)],
    keyword: Annotated[str | None, Query(max_length=50)] = None,
    limit: Annotated[int, Query(ge=1, le=100)] = 10,
    sort: str = &quot;latest&quot;
):
    return {
        &quot;user_id&quot;: user_id,
        &quot;keyword&quot;: keyword,
        &quot;limit&quot;: limit,
        &quot;sort&quot;: sort
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예제는 특정 사용자의 게시글 목록을 조회하면서 검색어, 개수 제한, 정렬 기준까지 함께 받는 형태다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청 예시는 다음과 같다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;/users/1/posts?keyword=fastapi&amp;amp;limit=20&amp;amp;sort=latest&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;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;값&lt;/th&gt;
&lt;th&gt;종류&lt;/th&gt;
&lt;th&gt;의미&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;user_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Path Parameter&lt;/td&gt;
&lt;td&gt;어떤 사용자의 게시글인지 지정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;keyword&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Query Parameter&lt;/td&gt;
&lt;td&gt;검색어&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;limit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Query Parameter&lt;/td&gt;
&lt;td&gt;가져올 개수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sort&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Query Parameter&lt;/td&gt;
&lt;td&gt;정렬 기준&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;user_id&lt;/code&gt;는 URL 경로에서 빠지면 API가 성립하지 않는다.&lt;br /&gt;반면 &lt;code&gt;keyword&lt;/code&gt;, &lt;code&gt;limit&lt;/code&gt;, &lt;code&gt;sort&lt;/code&gt;는 조회 조건이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 구분하면 API 주소가 훨씬 읽기 쉬워진다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;FAQ&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Path Parameter와 Query Parameter는 둘 다 GET 요청에서만 쓰나요?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아니다. GET 요청에서 자주 보일 뿐이다. Path Parameter는 URL 경로의 일부이므로 다른 HTTP 메서드에서도 사용할 수 있다. 예를 들어 &lt;code&gt;DELETE /posts/10&lt;/code&gt;처럼 특정 게시글을 삭제할 때도 Path Parameter를 쓴다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Query Parameter는 항상 선택값인가요?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아니다. 기본값을 주면 선택값이 되지만, 기본값 없이 선언하면 필수값이 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Path Parameter에 기본값을 줄 수 있나요?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Path Parameter는 URL 경로의 일부라서 항상 필요하다. 선택값처럼 쓰고 싶다면 Query Parameter로 설계하는 편이 자연스럽다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;code&gt;Path&lt;/code&gt;, &lt;code&gt;Query&lt;/code&gt;는 꼭 써야 하나요?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적인 값 전달만 할 때는 꼭 필요하지 않다. 하지만 최소값, 최대값, 문자열 길이, 별칭, 설명 같은 조건을 추가하려면 &lt;code&gt;Path&lt;/code&gt;, &lt;code&gt;Query&lt;/code&gt;를 쓰는 편이 좋다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;code&gt;Annotated&lt;/code&gt;를 꼭 써야 하나요?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 예제에서는 꼭 필요하지 않다. 다만 &lt;code&gt;Path&lt;/code&gt;, &lt;code&gt;Query&lt;/code&gt;로 검증 조건을 함께 넣을 때는 &lt;code&gt;Annotated&lt;/code&gt;를 쓰면 타입과 검증 조건을 분리해서 읽기 좋다. FastAPI 공식 문서에서도 최신 예제에서는 &lt;code&gt;Annotated&lt;/code&gt; 사용을 권장하는 흐름이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;마무리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI에서 Path Parameter는 &lt;b&gt;특정 리소스를 지정하는 값&lt;/b&gt;에 어울린다.&lt;br /&gt;Query Parameter는 &lt;b&gt;조회 조건을 조절하는 값&lt;/b&gt;에 어울린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글을 작성하거나 코드를 짤 때는 이 기준만 먼저 적용해도 대부분의 API 주소가 자연스럽게 정리된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 사용자, 게시글, 상품처럼 하나를 가리키면 Path Parameter&lt;/li&gt;
&lt;li&gt;검색어, 정렬, 필터, 페이지네이션처럼 조건을 바꾸면 Query Parameter&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI는 타입 힌트만으로도 값 변환과 검증을 처리해준다. 여기에 &lt;code&gt;Path&lt;/code&gt;, &lt;code&gt;Query&lt;/code&gt;, &lt;code&gt;Annotated&lt;/code&gt;를 함께 쓰면 실무에서 쓰기 좋은 API 형태로 조금 더 단단하게 다듬을 수 있다.&lt;/p&gt;</description>
      <category>개발/FastAPI</category>
      <category>fastapi</category>
      <category>Python</category>
      <category>개발도구</category>
      <category>코딩입문</category>
      <category>파이썬</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/47</guid>
      <comments>https://notebase.tistory.com/entry/fastapi-path-query-parameter#entry47comment</comments>
      <pubDate>Sun, 24 May 2026 12:17:01 +0900</pubDate>
    </item>
    <item>
      <title>FastAPI GET, POST 요청 이해하기: 조회와 데이터 전송의 차이</title>
      <link>https://notebase.tistory.com/entry/fastapi-get-post-request</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI에서 GET과 POST 요청이 어떻게 다른지, 조회 API와 데이터 생성 API를 직접 만들며 Request Body, Pydantic, HTTPException, 자동 문서 테스트 흐름까지 초보자 기준으로 정리했습니다.&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;FastAPI GET, POST 요청 차이는 API를 만들 때 가장 먼저 헷갈리는 부분입니다. 간단히 보면 GET은 데이터를 조회할 때, POST는 새 데이터를 보내거나 생성할 때 주로 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 &lt;code&gt;@app.get()&lt;/code&gt;과 &lt;code&gt;@app.post()&lt;/code&gt;가 문법만 다른 것처럼 보일 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제로는 API의 목적이 다릅니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자가 상품 목록을 본다 &amp;rarr; &lt;code&gt;GET&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;사용자가 새 상품을 등록한다 &amp;rarr; &lt;code&gt;POST&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;게시글 상세 내용을 불러온다 &amp;rarr; &lt;code&gt;GET&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;회원가입 정보를 서버로 보낸다 &amp;rarr; &lt;code&gt;POST&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 차이를 이해하면 FastAPI 코드도 훨씬 자연스럽게 읽힙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;GET과 POST는 무엇이 다를까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API 요청은 클라이언트가 서버에 &amp;ldquo;무엇을 해달라&amp;rdquo;고 보내는 신호입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 클라이언트는 브라우저, 모바일 앱, 프론트엔드 화면, 다른 서버가 될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GET과 POST는 그 요청의 종류입니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;GET&lt;/th&gt;
&lt;th&gt;POST&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;주 용도&lt;/td&gt;
&lt;td&gt;데이터 조회&lt;/td&gt;
&lt;td&gt;데이터 생성 또는 전송&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;데이터 전달 위치&lt;/td&gt;
&lt;td&gt;URL 경로 또는 쿼리 파라미터&lt;/td&gt;
&lt;td&gt;Request Body&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;브라우저 주소창 테스트&lt;/td&gt;
&lt;td&gt;쉬움&lt;/td&gt;
&lt;td&gt;어렵거나 제한적&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FastAPI 문법&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@app.get()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@app.post()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;예시&lt;/td&gt;
&lt;td&gt;상품 목록 조회&lt;/td&gt;
&lt;td&gt;새 상품 등록&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점은 GET이 &amp;ldquo;가볍고&amp;rdquo;, POST가 &amp;ldquo;무겁다&amp;rdquo;는 식으로 외우는 게 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;GET은 서버에 있는 데이터를 가져오는 요청&lt;/b&gt;, &lt;b&gt;POST는 서버에 데이터를 보내는 요청&lt;/b&gt;에 가깝게 이해하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;실습 준비&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 이전 단계에서 FastAPI 설치와 첫 실행까지 끝낸 상태를 기준으로 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 폴더 구조는 아래처럼 두면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;fastapi-get-post/
├── .venv/
└── main.py&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가상환경이 아직 없다면 먼저 만듭니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;python -m venv .venv&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;Windows PowerShell에서는 아래처럼 활성화합니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;.venv\Scripts\activate.ps1&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;macOS, Linux에서는 아래 명령어를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;source .venv/bin/activate&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;FastAPI가 설치되어 있지 않다면 설치합니다.&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;pip install &quot;fastapi[standard]&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;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;fastapi dev main.py&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;code&gt;fastapi dev main.py&lt;/code&gt;는 개발용 실행 명령어입니다. 코드를 수정하고 저장하면 서버가 자동으로 다시 로드되므로, 매번 터미널에서 서버를 껐다 켤 필요는 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;GET 요청 만들기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 가장 단순한 GET API를 만들어보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;main.py&lt;/code&gt; 파일에 아래 코드를 작성합니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;# main.py
from fastapi import FastAPI

app = FastAPI()


@app.get(&quot;/&quot;)
def read_root():
    return {&quot;message&quot;: &quot;FastAPI GET POST example&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;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;fastapi dev main.py&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저에서 아래 주소로 접속합니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://127.0.0.1:8000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응답은 이렇게 나옵니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;message&quot;: &quot;FastAPI GET POST example&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;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;@app.get(&quot;/&quot;)
def read_root():
    return {&quot;message&quot;: &quot;FastAPI GET POST example&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;code&gt;@app.get(&quot;/&quot;)&lt;/code&gt;는 &lt;code&gt;/&lt;/code&gt; 주소로 GET 요청이 들어왔을 때 바로 아래 함수를 실행하라는 뜻입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저 주소창에 URL을 입력해서 접속하는 행동도 기본적으로 GET 요청에 가깝습니다. 그래서 GET API는 주소창에서 바로 테스트하기 쉽습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;상품 목록을 GET으로 조회하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 조금 더 API다운 예제를 만들어보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상품 목록을 조회하는 API입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;main.py&lt;/code&gt;를 아래처럼 수정합니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;# main.py
from fastapi import FastAPI

app = FastAPI()

items = [
    {&quot;id&quot;: 1, &quot;name&quot;: &quot;keyboard&quot;, &quot;price&quot;: 30000},
    {&quot;id&quot;: 2, &quot;name&quot;: &quot;mouse&quot;, &quot;price&quot;: 15000},
]


@app.get(&quot;/&quot;)
def read_root():
    return {&quot;message&quot;: &quot;FastAPI GET POST example&quot;}


@app.get(&quot;/items&quot;)
def read_items():
    return {&quot;items&quot;: items}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저에서 아래 주소로 접속합니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://127.0.0.1:8000/items&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응답은 이렇게 나옵니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;items&quot;: [
    {
      &quot;id&quot;: 1,
      &quot;name&quot;: &quot;keyboard&quot;,
      &quot;price&quot;: 30000
    },
    {
      &quot;id&quot;: 2,
      &quot;name&quot;: &quot;mouse&quot;,
      &quot;price&quot;: 15000
    }
  ]
}&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;이 API는 서버에 있는 &lt;code&gt;items&lt;/code&gt; 목록을 가져옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 &lt;code&gt;GET /items&lt;/code&gt;가 자연스럽습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;items&lt;/code&gt;는 임시 데이터입니다. 실제 서비스라면 데이터베이스에 저장된 상품 목록을 가져오겠지만, 처음에는 리스트로 흐름만 이해해도 충분합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Path Parameter로 특정 상품 조회하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상품 전체 목록이 아니라 특정 상품 하나만 조회하고 싶을 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래 주소처럼 상품 ID를 URL에 넣는 방식입니다.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;http://127.0.0.1:8000/items/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;Path Parameter&lt;/b&gt;라고 합니다. FastAPI 공식 문서에서도 &lt;code&gt;/items/{item_id}&lt;/code&gt; 같은 형식으로 path parameter를 선언하는 방식을 안내합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;main.py&lt;/code&gt;에 아래 코드를 추가합니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;# main.py
from fastapi import FastAPI, HTTPException

app = FastAPI()

items = [
    {&quot;id&quot;: 1, &quot;name&quot;: &quot;keyboard&quot;, &quot;price&quot;: 30000},
    {&quot;id&quot;: 2, &quot;name&quot;: &quot;mouse&quot;, &quot;price&quot;: 15000},
]


@app.get(&quot;/&quot;)
def read_root():
    return {&quot;message&quot;: &quot;FastAPI GET POST example&quot;}


@app.get(&quot;/items&quot;)
def read_items():
    return {&quot;items&quot;: items}


@app.get(&quot;/items/{item_id}&quot;)
def read_item(item_id: int):
    for item in items:
        if item[&quot;id&quot;] == item_id:
            return item

    raise HTTPException(status_code=404, detail=&quot;Item not found&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;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;http://127.0.0.1:8000/items/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;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;id&quot;: 1,
  &quot;name&quot;: &quot;keyboard&quot;,
  &quot;price&quot;: 30000
}&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;code&gt;2&lt;/code&gt;번 상품을 조회해봅니다.&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;http://127.0.0.1:8000/items/2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응답은 이렇게 바뀝니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;id&quot;: 2,
  &quot;name&quot;: &quot;mouse&quot;,
  &quot;price&quot;: 15000
}&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;없는 상품 ID를 조회하면 어떻게 될까요?&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;http://127.0.0.1:8000/items/999&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우에는 아래 코드가 실행됩니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;raise HTTPException(status_code=404, detail=&quot;Item not found&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;code&gt;HTTPException&lt;/code&gt;은 API에서 에러 응답을 명확하게 내려줄 때 사용합니다. 여기서는 상품을 찾지 못했으므로 &lt;code&gt;404 Not Found&lt;/code&gt; 상태 코드를 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전처럼 단순히 &lt;code&gt;{&quot;message&quot;: &quot;Item not found&quot;}&lt;/code&gt;를 반환할 수도 있습니다. 하지만 그러면 실제로는 상품을 못 찾았는데도 HTTP 상태 코드는 &lt;code&gt;200 OK&lt;/code&gt;가 될 수 있습니다. API를 설계할 때는 이런 부분을 구분해주는 편이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 봐야 할 또 다른 부분은 &lt;code&gt;item_id: int&lt;/code&gt;입니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;def read_item(item_id: int):&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;FastAPI는 URL로 들어온 값이 정수인지 확인합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &lt;code&gt;/items/abc&lt;/code&gt;처럼 숫자가 아닌 값을 넣으면 FastAPI는 &lt;code&gt;item_id&lt;/code&gt;를 정수로 바꿀 수 없다고 판단하고 &lt;code&gt;422&lt;/code&gt; 검증 에러를 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응답 형식은 FastAPI와 Pydantic 버전에 따라 조금 다를 수 있지만, 대략 아래처럼 &lt;code&gt;item_id&lt;/code&gt; 위치에서 정수 변환에 실패했다는 내용이 포함됩니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;detail&quot;: [
    {
      &quot;loc&quot;: [&quot;path&quot;, &quot;item_id&quot;],
      &quot;msg&quot;: &quot;Input should be a valid integer&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;이게 FastAPI의 장점 중 하나입니다. Python 타입 힌트를 적으면 요청 데이터 검증과 문서화에 함께 활용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Query Parameter로 검색 조건 받기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GET 요청에서는 URL 뒤에 조건을 붙여서 데이터를 조회할 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래 주소를 봅니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://127.0.0.1:8000/search?keyword=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;&lt;code&gt;?keyword=key&lt;/code&gt; 부분이 Query Parameter입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색어, 페이지 번호, 정렬 조건처럼 &amp;ldquo;조회 조건&amp;rdquo;을 보낼 때 자주 사용합니다. FastAPI 공식 문서에서도 함수 인자 중 path에 포함되지 않은 단순 타입 값은 query parameter로 해석된다고 설명합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;main.py&lt;/code&gt;를 아래처럼 수정합니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;# main.py
from fastapi import FastAPI, HTTPException

app = FastAPI()

items = [
    {&quot;id&quot;: 1, &quot;name&quot;: &quot;keyboard&quot;, &quot;price&quot;: 30000},
    {&quot;id&quot;: 2, &quot;name&quot;: &quot;mouse&quot;, &quot;price&quot;: 15000},
]


@app.get(&quot;/&quot;)
def read_root():
    return {&quot;message&quot;: &quot;FastAPI GET POST example&quot;}


@app.get(&quot;/items&quot;)
def read_items():
    return {&quot;items&quot;: items}


@app.get(&quot;/items/{item_id}&quot;)
def read_item(item_id: int):
    for item in items:
        if item[&quot;id&quot;] == item_id:
            return item

    raise HTTPException(status_code=404, detail=&quot;Item not found&quot;)


@app.get(&quot;/search&quot;)
def search_items(keyword: str = &quot;&quot;):
    result = []

    for item in items:
        if keyword.lower() in item[&quot;name&quot;].lower():
            result.append(item)

    return {&quot;keyword&quot;: keyword, &quot;result&quot;: result}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저에서 아래 주소로 접속합니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://127.0.0.1:8000/search?keyword=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;응답은 이렇게 나옵니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;keyword&quot;: &quot;key&quot;,
  &quot;result&quot;: [
    {
      &quot;id&quot;: 1,
      &quot;name&quot;: &quot;keyboard&quot;,
      &quot;price&quot;: 30000
    }
  ]
}&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;code&gt;keyword&lt;/code&gt; 값을 바꿔보면 결과도 바뀝니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://127.0.0.1:8000/search?keyword=mouse&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;이렇게 GET 요청은 데이터를 조회하면서 조건을 붙이는 데 잘 어울립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;POST 요청 만들기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 POST 요청을 만들어보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;POST는 보통 클라이언트가 서버에 데이터를 보낼 때 사용합니다. 예를 들어 새 상품 등록, 회원가입, 게시글 작성 같은 작업입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GET은 주소창에서 쉽게 테스트할 수 있었지만, POST는 보통 Request Body에 JSON 데이터를 담아 보냅니다. 그래서 브라우저 주소창만으로는 테스트하기 어렵고, FastAPI의 &lt;code&gt;/docs&lt;/code&gt; 화면이나 Postman 같은 도구를 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI에서는 Request Body를 받을 때 Pydantic의 &lt;code&gt;BaseModel&lt;/code&gt;을 자주 사용합니다. 공식 문서에서도 Pydantic 모델을 함수 인자로 선언하면 해당 값이 request body로 처리된다고 안내합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;main.py&lt;/code&gt;를 아래처럼 수정합니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;# main.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    price: int


items = [
    {&quot;id&quot;: 1, &quot;name&quot;: &quot;keyboard&quot;, &quot;price&quot;: 30000},
    {&quot;id&quot;: 2, &quot;name&quot;: &quot;mouse&quot;, &quot;price&quot;: 15000},
]


@app.get(&quot;/&quot;)
def read_root():
    return {&quot;message&quot;: &quot;FastAPI GET POST example&quot;}


@app.get(&quot;/items&quot;)
def read_items():
    return {&quot;items&quot;: items}


@app.get(&quot;/items/{item_id}&quot;)
def read_item(item_id: int):
    for item in items:
        if item[&quot;id&quot;] == item_id:
            return item

    raise HTTPException(status_code=404, detail=&quot;Item not found&quot;)


@app.post(&quot;/items&quot;)
def create_item(item: Item):
    new_item = {
        &quot;id&quot;: len(items) + 1,
        &quot;name&quot;: item.name,
        &quot;price&quot;: item.price,
    }

    items.append(new_item)

    return new_item&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로 추가된 부분은 두 군데입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 요청으로 받을 데이터 형식을 정의합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class Item(BaseModel):
    name: str
    price: int&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;ldquo;상품을 등록할 때 &lt;code&gt;name&lt;/code&gt;은 문자열, &lt;code&gt;price&lt;/code&gt;는 정수로 받겠다&amp;rdquo;는 뜻입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 POST API입니다.&lt;/p&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;@app.post(&quot;/items&quot;)
def create_item(item: Item):
    new_item = {
        &quot;id&quot;: len(items) + 1,
        &quot;name&quot;: item.name,
        &quot;price&quot;: item.price,
    }

    items.append(new_item)

    return new_item&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;code&gt;@app.post(&quot;/items&quot;)&lt;/code&gt;는 &lt;code&gt;/items&lt;/code&gt; 주소로 POST 요청이 들어왔을 때 실행됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GET으로 &lt;code&gt;/items&lt;/code&gt;에 접근하면 상품 목록을 조회하고, POST로 &lt;code&gt;/items&lt;/code&gt;에 요청하면 새 상품을 추가하는 구조입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주소는 같아도 요청 방식이 다르면 다른 API처럼 동작할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;code&gt;/docs&lt;/code&gt;에서 POST 요청 테스트하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;POST 요청은 FastAPI 자동 문서에서 테스트하는 편이 가장 쉽습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버가 실행 중인 상태에서 아래 주소로 들어갑니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://127.0.0.1:8000/docs&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;POST /items&lt;/b&gt; 항목을 찾습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 순서대로 진행합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;**&lt;code&gt;POST /items&lt;/code&gt;** 항목을 클릭합니다.&lt;/li&gt;
&lt;li&gt;**&lt;code&gt;Try it out&lt;/code&gt;** 버튼을 누릅니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Request body&lt;/b&gt; 영역에 JSON 데이터를 입력합니다.&lt;/li&gt;
&lt;li&gt;**&lt;code&gt;Execute&lt;/code&gt;** 버튼을 누릅니다.&lt;/li&gt;
&lt;li&gt;하단의 &lt;b&gt;Response body&lt;/b&gt;에서 응답 결과를 확인합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Request body에는 아래처럼 입력합니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;monitor&quot;,
  &quot;price&quot;: 200000
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성공하면 응답은 아래처럼 나옵니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;id&quot;: 3,
  &quot;name&quot;: &quot;monitor&quot;,
  &quot;price&quot;: 200000
}&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;이제 다시 GET &lt;code&gt;/items&lt;/code&gt;를 실행해보면 새 상품이 목록에 추가된 것을 볼 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;items&quot;: [
    {
      &quot;id&quot;: 1,
      &quot;name&quot;: &quot;keyboard&quot;,
      &quot;price&quot;: 30000
    },
    {
      &quot;id&quot;: 2,
      &quot;name&quot;: &quot;mouse&quot;,
      &quot;price&quot;: 15000
    },
    {
      &quot;id&quot;: 3,
      &quot;name&quot;: &quot;monitor&quot;,
      &quot;price&quot;: 200000
    }
  ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 이 예제는 메모리 안의 리스트에 데이터를 추가하는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버를 껐다가 다시 켜면 추가한 데이터는 사라집니다. 실제 서비스에서는 이런 데이터를 데이터베이스에 저장해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;GET과 POST를 같은 주소로 쓰는 이유&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 보면 헷갈릴 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;@app.get(&quot;/items&quot;)
def read_items():
    return {&quot;items&quot;: items}


@app.post(&quot;/items&quot;)
def create_item(item: Item):
    ...&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;code&gt;/items&lt;/code&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 하나는 GET이고, 하나는 POST입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조는 실제 API에서 자연스럽게 쓰입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;GET /items&lt;/code&gt; &amp;rarr; 상품 목록 조회&lt;/li&gt;
&lt;li&gt;&lt;code&gt;POST /items&lt;/code&gt; &amp;rarr; 새 상품 등록&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /items/1&lt;/code&gt; &amp;rarr; 1번 상품 조회&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주소는 리소스를 나타내고, HTTP 메서드는 행동을 나타낸다고 보면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;/items&lt;/code&gt;는 &amp;ldquo;상품들&amp;rdquo;이라는 자원입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 상품들을 조회하면 GET이고, 상품을 새로 추가하면 POST입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;GET으로 데이터를 보내면 안 될까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GET도 query parameter를 통해 값을 보낼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 검색어는 이렇게 보낼 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;/search?keyword=keyboard&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;하지만 상품 등록처럼 구조가 있는 데이터는 GET보다는 POST가 어울립니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;monitor&quot;,
  &quot;price&quot;: 200000
}&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;이런 데이터는 URL에 붙이기보다 Request Body에 담아 보내는 편이 자연스럽습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 회원가입, 게시글 작성, 주문 생성처럼 서버 상태가 바뀌는 작업은 GET보다 POST로 설계하는 게 일반적입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 오해하기 쉬운 부분이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;POST Body에 넣는다고 해서 자동으로 보안이 보장되는 것은 아닙니다. URL에 그대로 보이지 않을 뿐입니다. 개발자 도구의 Network 탭을 보면 POST Body에 담긴 값도 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 서비스에서 민감한 데이터를 다룬다면 HTTPS, 인증, 권한 확인 같은 보안 처리가 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;잘못된 데이터가 들어오면 어떻게 될까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Item&lt;/code&gt; 모델은 &lt;code&gt;name&lt;/code&gt;을 문자열, &lt;code&gt;price&lt;/code&gt;를 정수로 받도록 정의했습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class Item(BaseModel):
    name: str
    price: int&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;code&gt;/docs&lt;/code&gt;에서 아래처럼 잘못된 데이터를 보내봅니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;monitor&quot;,
  &quot;price&quot;: &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;그러면 FastAPI는 요청을 그대로 처리하지 않고 검증 에러를 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;price&lt;/code&gt;는 정수여야 하는데 문자열이 들어왔기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분이 FastAPI와 Pydantic을 함께 사용할 때 편한 지점입니다. 요청 데이터의 형식을 코드에 적어두면 FastAPI가 자동으로 검증하고, API 문서에도 해당 구조를 보여줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;전체 코드&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 작성한 전체 코드는 아래와 같습니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;# main.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    price: int


items = [
    {&quot;id&quot;: 1, &quot;name&quot;: &quot;keyboard&quot;, &quot;price&quot;: 30000},
    {&quot;id&quot;: 2, &quot;name&quot;: &quot;mouse&quot;, &quot;price&quot;: 15000},
]


@app.get(&quot;/&quot;)
def read_root():
    return {&quot;message&quot;: &quot;FastAPI GET POST example&quot;}


@app.get(&quot;/items&quot;)
def read_items():
    return {&quot;items&quot;: items}


@app.get(&quot;/items/{item_id}&quot;)
def read_item(item_id: int):
    for item in items:
        if item[&quot;id&quot;] == item_id:
            return item

    raise HTTPException(status_code=404, detail=&quot;Item not found&quot;)


@app.get(&quot;/search&quot;)
def search_items(keyword: str = &quot;&quot;):
    result = []

    for item in items:
        if keyword.lower() in item[&quot;name&quot;].lower():
            result.append(item)

    return {&quot;keyword&quot;: keyword, &quot;result&quot;: result}


@app.post(&quot;/items&quot;)
def create_item(item: Item):
    new_item = {
        &quot;id&quot;: len(items) + 1,
        &quot;name&quot;: item.name,
        &quot;price&quot;: item.price,
    }

    items.append(new_item)

    return new_item&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 실행 명령어는 아래와 같습니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;fastapi dev main.py&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동 문서 화면은 아래 주소에서 확인합니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://127.0.0.1:8000/docs&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;자주 헷갈리는 부분&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;GET과 POST는 주소가 달라야 하나요?&lt;/b&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;&lt;code&gt;GET /items&lt;/code&gt;와 &lt;code&gt;POST /items&lt;/code&gt;처럼 주소가 같아도 됩니다.&lt;br /&gt;요청 방식이 다르기 때문에 FastAPI는 서로 다른 함수로 처리합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;POST 요청은 브라우저 주소창에서 테스트할 수 있나요?&lt;/b&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;주소창에 URL을 입력하는 것은 보통 GET 요청입니다. POST 요청은 &lt;code&gt;/docs&lt;/code&gt;, Postman, curl, 프론트엔드 코드 등을 사용해서 테스트하는 편이 좋습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Request Body와 Query Parameter는 뭐가 다른가요?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Query Parameter는 URL에 붙는 값입니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;/search?keyword=keyboard&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;Request Body는 요청 안에 담겨서 전달되는 데이터입니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;keyboard&quot;,
  &quot;price&quot;: 30000
}&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;검색, 필터링, 페이지 번호처럼 간단한 조회 조건은 Query Parameter가 어울립니다.&lt;br /&gt;새 데이터를 생성하거나 구조가 있는 데이터를 보낼 때는 Request Body가 더 자연스럽습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;code&gt;BaseModel&lt;/code&gt;은 왜 필요한가요?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;BaseModel&lt;/code&gt;은 요청 데이터의 형태를 정의하는 데 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class Item(BaseModel):
    name: str
    price: int&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;이렇게 작성하면 FastAPI가 요청 데이터를 검증하고, &lt;code&gt;/docs&lt;/code&gt; 문서에도 어떤 JSON을 보내야 하는지 표시해줍니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;code&gt;HTTPException&lt;/code&gt;은 왜 사용하나요?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API에서 에러 상황을 HTTP 상태 코드로 명확하게 알려주기 위해 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 없는 상품을 조회했을 때는 단순 메시지보다 &lt;code&gt;404 Not Found&lt;/code&gt;를 반환하는 편이 더 자연스럽습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;raise HTTPException(status_code=404, detail=&quot;Item not found&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;FastAPI에서는 &lt;code&gt;HTTPException&lt;/code&gt;을 &lt;code&gt;return&lt;/code&gt;하는 것이 아니라 &lt;code&gt;raise&lt;/code&gt;해서 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;처음에는 이렇게 구분하면 된다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI에서 GET과 POST를 처음 배울 때는 너무 복잡하게 외울 필요가 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 기준만 잡아도 대부분의 입문 예제는 이해할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;GET  &amp;rarr; 서버에서 데이터를 가져온다
POST &amp;rarr; 서버로 데이터를 보낸다&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;상품 목록을 본다면 GET입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새 상품을 등록한다면 POST입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색어처럼 간단한 조건은 URL의 Query Parameter로 보내고, 상품 정보처럼 구조가 있는 데이터는 POST의 Request Body로 보냅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;없는 상품처럼 요청 결과를 정상으로 처리하기 어려운 경우에는 &lt;code&gt;HTTPException&lt;/code&gt;으로 적절한 상태 코드를 반환할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 흐름이 잡히면 다음 단계로는 &lt;code&gt;PUT&lt;/code&gt;, &lt;code&gt;PATCH&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt; 요청을 이어서 배우기 좋습니다. 그때부터는 단순 조회와 생성뿐 아니라 수정, 일부 수정, 삭제까지 API 구조가 조금 더 실제 서비스에 가까워집니다.&lt;/p&gt;</description>
      <category>개발/FastAPI</category>
      <category>fastapi</category>
      <category>Python</category>
      <category>코딩입문</category>
      <category>파이썬</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/46</guid>
      <comments>https://notebase.tistory.com/entry/fastapi-get-post-request#entry46comment</comments>
      <pubDate>Sun, 24 May 2026 11:50:34 +0900</pubDate>
    </item>
    <item>
      <title>FastAPI 설치와 시작하기: 첫 API 만들고 문서 확인까지</title>
      <link>https://notebase.tistory.com/entry/fastapi-install-first-api</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Python FastAPI 설치부터 가상환경 생성, 첫 API main.py 코드 작성, 서버 실행, 자동 API 문서(docs) 확인까지 초보자 기준으로 직접 따라 할 수 있게 정리한 입문 가이드입니다.&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;FastAPI 설치를 끝내고 첫 API를 만드는 과정은 생각보다 짧습니다. Python 파일 하나를 만들고, 서버를 실행한 뒤 브라우저에서 응답을 확인하면 기본 흐름은 잡힙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI는 Python으로 API 서버를 만들 때 자주 쓰이는 웹 프레임워크입니다. Django처럼 큰 웹 서비스 전체를 만들기보다는, 모바일 앱이나 프론트엔드에서 호출할 API 서버를 빠르게 만들 때 많이 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 배우는 입장에서는 자동 문서 기능이 특히 편합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 조금만 작성해도 API 문서 화면이 자동으로 만들어집니다. 그래서 내가 만든 API가 제대로 동작하는지 브라우저에서 바로 테스트할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;FastAPI를 설치하기 전에 필요한 것&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Python이 설치되어 있어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널이나 명령 프롬프트에서 아래 명령어를 입력해 봅니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;python --version&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경에 따라 아래 명령어가 필요할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;python3 --version&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;버전이 출력되면 Python은 설치된 상태입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 이런 식입니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Python 3.12.3&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;버전이 나오지 않는다면 Python부터 설치해야 합니다. Windows라면 Python 공식 사이트에서 설치할 수 있고, macOS에서는 Homebrew나 공식 설치 파일을 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점은 FastAPI 자체보다 Python 실행 환경을 먼저 정리하는 것입니다. 설치는 됐는데 &lt;code&gt;python&lt;/code&gt; 명령어가 안 먹히면, FastAPI 문제가 아니라 Python 경로 설정 문제일 가능성이 큽니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;프로젝트 폴더 만들기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 FastAPI 실습용 폴더를 하나 만듭니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;mkdir fastapi-first-api
cd fastapi-first-api&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;폴더 이름은 꼭 같을 필요는 없습니다. 다만 처음에는 한글이나 공백이 들어간 폴더명보다 영어 소문자 위주로 만드는 편이 덜 헷갈립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 이런 구조로 시작합니다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;fastapi-first-api/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 파일은 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;가상환경 만들기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python 프로젝트에서는 가상환경을 만들어 두는 편이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가상환경은 프로젝트마다 패키지를 따로 설치할 수 있게 해주는 공간입니다. 예를 들어 A 프로젝트에서는 FastAPI를 쓰고, B 프로젝트에서는 Django를 쓰더라도 서로 영향을 덜 받습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 명령어로 가상환경을 만듭니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;python -m venv .venv&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;macOS나 Linux에서 &lt;code&gt;python&lt;/code&gt; 명령어가 안 되면 아래처럼 입력합니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;python3 -m venv .venv&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;Windows PowerShell 기준입니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;.venv\Scripts\activate.ps1&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;Windows 명령 프롬프트라면 아래 명령어를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;taggerscript&quot;&gt;&lt;code&gt;.venv\Scripts\activate&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;macOS, Linux에서는 아래처럼 실행합니다.&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;source .venv/bin/activate&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;code&gt;(.venv)&lt;/code&gt; 같은 표시가 붙습니다.&lt;/p&gt;
&lt;pre class=&quot;gcode&quot;&gt;&lt;code&gt;(.venv) fastapi-first-api %&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 표시가 보이면 현재 프로젝트 안의 가상환경을 사용 중이라는 뜻입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;FastAPI 설치하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 FastAPI를 설치합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2026년 5월 기준 FastAPI 공식 문서는 아래 명령어를 안내하고 있습니다.&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;pip install &quot;fastapi[standard]&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;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;pip install fastapi uvicorn&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;code&gt;fastapi[standard]&lt;/code&gt;를 설치하는 편이 덜 헷갈립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;fastapi[standard]&lt;/code&gt;로 설치하면 FastAPI를 실행하고 개발하는 데 필요한 표준 의존성이 함께 설치됩니다. 그래서 이 글에서도 이 방식을 기준으로 진행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치가 끝났는지 확인하려면 아래 명령어를 입력합니다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;fastapi --version&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버전 정보가 출력되면 설치가 된 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;첫 API 파일 만들기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 &lt;code&gt;main.py&lt;/code&gt; 파일을 만듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 폴더 안이 아래와 같은 구조가 되도록 파일을 생성해 주세요.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;fastapi-first-api/
├── .venv/
└── main.py&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;code&gt;main.py&lt;/code&gt; 파일에 아래 코드를 작성하고 저장합니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;# main.py
from fastapi import FastAPI

app = FastAPI()


@app.get(&quot;/&quot;)
def read_root():
    return {&quot;message&quot;: &quot;Hello FastAPI&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;코드는 짧지만 FastAPI의 기본 구조가 들어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드에서 봐야 할 부분은 세 가지입니다. - &lt;code&gt;from fastapi import FastAPI&lt;/code&gt;: FastAPI 클래스를 가져옵니다. - &lt;code&gt;app = FastAPI()&lt;/code&gt;: API 서버 역할을 할 앱 객체를 만듭니다. - &lt;code&gt;@app.get(&quot;/&quot;)&lt;/code&gt;: &lt;code&gt;/&lt;/code&gt; 주소로 들어온 GET 요청을 아래 함수와 연결합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 함수는 실제 응답을 반환합니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;def read_root():
    return {&quot;message&quot;: &quot;Hello FastAPI&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;여기서는 Python 딕셔너리를 반환했지만, 브라우저에서는 JSON 형태로 응답이 보입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;FastAPI 서버 실행하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 서버를 실행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널에서 &lt;code&gt;main.py&lt;/code&gt;가 있는 폴더 위치인지 확인한 뒤 아래 명령어를 입력합니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;fastapi dev main.py&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상적으로 실행되면 터미널에 서버 주소가 표시됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 아래 주소를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://127.0.0.1:8000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저 주소창에 아래 주소를 입력합니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://127.0.0.1:8000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화면에 아래와 비슷한 응답이 나오면 성공입니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;message&quot;: &quot;Hello FastAPI&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;이게 첫 번째 API입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 데이터베이스도 없고, 로그인도 없고, 복잡한 구조도 없습니다. 하지만 브라우저가 API 서버에 요청을 보내고, FastAPI가 JSON 응답을 돌려주는 흐름은 이미 만들어졌습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;자동 API 문서 확인하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI의 장점 중 하나는 API 문서를 자동으로 만들어준다는 점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버가 실행 중인 상태에서 아래 주소로 접속해 봅니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://127.0.0.1:8000/docs&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;그러면 Swagger UI 화면이 열립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;/&lt;/code&gt; API를 직접 실행해볼 수 있습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;**&lt;code&gt;GET /&lt;/code&gt;** 항목을 클릭합니다.&lt;/li&gt;
&lt;li&gt;**&lt;code&gt;Try it out&lt;/code&gt;** 버튼을 누릅니다.&lt;/li&gt;
&lt;li&gt;**&lt;code&gt;Execute&lt;/code&gt;** 버튼을 누릅니다.&lt;/li&gt;
&lt;li&gt;하단의 &lt;b&gt;Response body&lt;/b&gt;에서 응답 결과를 확인합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드로 만든 API를 브라우저 문서 화면에서 바로 테스트할 수 있는 구조입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ReDoc 문서도 기본으로 제공됩니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://127.0.0.1:8000/redoc&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;code&gt;/docs&lt;/code&gt;는 테스트하기 좋고, &lt;code&gt;/redoc&lt;/code&gt;은 문서처럼 읽기 좋습니다. 처음 배울 때는 &lt;code&gt;/docs&lt;/code&gt;를 더 자주 보게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;경로를 하나 더 추가해보기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 API를 하나 더 만들어보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;main.py&lt;/code&gt;를 아래처럼 수정합니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;# main.py
from fastapi import FastAPI

app = FastAPI()


@app.get(&quot;/&quot;)
def read_root():
    return {&quot;message&quot;: &quot;Hello FastAPI&quot;}


@app.get(&quot;/items&quot;)
def read_items():
    return {
        &quot;items&quot;: [
            {&quot;id&quot;: 1, &quot;name&quot;: &quot;keyboard&quot;},
            {&quot;id&quot;: 2, &quot;name&quot;: &quot;mouse&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;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://127.0.0.1:8000/items&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래처럼 응답이 나옵니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;items&quot;: [
    {
      &quot;id&quot;: 1,
      &quot;name&quot;: &quot;keyboard&quot;
    },
    {
      &quot;id&quot;: 2,
      &quot;name&quot;: &quot;mouse&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;code&gt;/docs&lt;/code&gt;에 다시 들어가면 &lt;code&gt;GET /items&lt;/code&gt; API가 자동으로 추가되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점은 문서를 따로 작성하지 않았다는 것입니다. FastAPI가 코드 구조를 보고 API 문서를 만들어줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;쿼리 파라미터 받아보기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API는 보통 클라이언트에서 값을 받아 처리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 &lt;code&gt;name&lt;/code&gt; 값을 받아서 응답하는 API를 만들어보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;main.py&lt;/code&gt;를 아래처럼 수정합니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;# main.py
from fastapi import FastAPI

app = FastAPI()


@app.get(&quot;/&quot;)
def read_root():
    return {&quot;message&quot;: &quot;Hello FastAPI&quot;}


@app.get(&quot;/items&quot;)
def read_items():
    return {
        &quot;items&quot;: [
            {&quot;id&quot;: 1, &quot;name&quot;: &quot;keyboard&quot;},
            {&quot;id&quot;: 2, &quot;name&quot;: &quot;mouse&quot;},
        ]
    }


@app.get(&quot;/hello&quot;)
def say_hello(name: str = &quot;FastAPI&quot;):
    return {&quot;message&quot;: f&quot;Hello, {name}&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;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://127.0.0.1:8000/hello&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응답은 이렇게 나옵니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;message&quot;: &quot;Hello, FastAPI&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;code&gt;name&lt;/code&gt; 값을 붙여봅니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://127.0.0.1:8000/hello?name=Python&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 응답이 바뀝니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;message&quot;: &quot;Hello, Python&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;code&gt;name: str = &quot;FastAPI&quot;&lt;/code&gt; 부분이 핵심입니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;def say_hello(name: str = &quot;FastAPI&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;FastAPI는 함수 인자를 보고 쿼리 파라미터로 처리합니다. &lt;code&gt;name&lt;/code&gt; 값이 들어오면 그 값을 사용하고, 없으면 기본값인 &lt;code&gt;&quot;FastAPI&quot;&lt;/code&gt;를 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분이 FastAPI를 처음 배울 때 꽤 중요한 감각입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API 주소, 함수 인자, 응답 데이터가 어떻게 연결되는지 이해하면 이후 경로 파라미터, 요청 본문, 데이터 검증도 훨씬 쉽게 이어집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;서버와 가상환경 종료하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 서버를 끄려면 터미널에서 아래 키를 누릅니다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;Ctrl + C&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버가 종료되면 브라우저에서 접속해도 응답이 나오지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 실행하려면 같은 명령어를 입력하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;fastapi dev main.py&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버를 종료한 뒤 가상환경에서도 빠져나가고 싶다면 아래 명령어를 입력합니다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;deactivate&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;다시 FastAPI 작업을 하려면 가상환경을 다시 활성화한 뒤 서버를 실행하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;자주 막히는 부분&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;code&gt;fastapi&lt;/code&gt; 명령어를 찾을 수 없다고 나오는 경우&lt;/b&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;터미널 앞에 &lt;code&gt;(.venv)&lt;/code&gt; 표시가 없다면 다시 활성화합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Windows PowerShell:&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;.venv\Scripts\activate.ps1&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;macOS, Linux:&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;source .venv/bin/activate&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;그래도 안 된다면 FastAPI가 현재 가상환경에 설치되지 않았을 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;pip install &quot;fastapi[standard]&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;code&gt;python&lt;/code&gt; 명령어가 안 되는 경우&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;macOS나 Linux에서는 &lt;code&gt;python3&lt;/code&gt;를 사용해야 할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;python3 -m venv .venv&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;Windows에서는 Python 설치 시 &lt;code&gt;Add python.exe to PATH&lt;/code&gt; 옵션이 빠졌을 가능성이 있습니다. 이 경우 Python을 다시 설치하거나 환경 변수를 확인해야 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;브라우저에서 접속이 안 되는 경우&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버가 실행 중인지 확인합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널에 서버 실행 로그가 떠 있어야 합니다. 서버를 실행한 터미널을 닫았거나 &lt;code&gt;Ctrl + C&lt;/code&gt;로 종료했다면 다시 실행해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;fastapi dev main.py&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 주소를 정확히 입력했는지도 확인합니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://127.0.0.1:8000&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;code&gt;https&lt;/code&gt;가 아니라 &lt;code&gt;http&lt;/code&gt;입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;8000번 포트가 이미 사용 중이라고 나오는 경우&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;fastapi dev main.py&lt;/code&gt;를 실행했을 때 8000번 포트가 이미 사용 중이라는 에러가 나올 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 실행한 FastAPI 서버가 아직 종료되지 않았거나, 다른 프로그램이 같은 포트를 사용 중일 때 생깁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 기존 서버가 실행 중인 터미널에서 &lt;code&gt;Ctrl + C&lt;/code&gt;를 눌러 종료해 봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 문제가 계속되면 다른 포트로 실행할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;fastapi dev main.py --port 8001&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 브라우저에서는 아래 주소로 접속합니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;http://127.0.0.1:8001&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;포트 번호만 바뀌었을 뿐, API 코드가 달라지는 것은 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;처음에는 여기까지만 해도 충분하다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI를 처음 배울 때 바로 데이터베이스, 로그인, 배포까지 가면 구조가 복잡해집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 아래 흐름을 확실히 잡는 게 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;가상환경 생성
&amp;rarr; FastAPI 설치
&amp;rarr; main.py 작성
&amp;rarr; fastapi dev main.py 실행
&amp;rarr; 브라우저에서 API 응답 확인
&amp;rarr; /docs에서 테스트&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;FastAPI는 처음 진입 장벽이 낮은 편이지만, 실제 백엔드 개발로 가면 구조 설계가 중요해집니다. 그래서 처음에는 많은 기능을 한 번에 외우기보다, API 하나가 요청을 받고 응답을 돌려주는 흐름부터 손에 익히는 편이 낫습니다.&lt;/p&gt;</description>
      <category>개발/FastAPI</category>
      <category>fastapi</category>
      <category>Python</category>
      <category>코딩입문</category>
      <category>파이썬</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/45</guid>
      <comments>https://notebase.tistory.com/entry/fastapi-install-first-api#entry45comment</comments>
      <pubDate>Sun, 24 May 2026 11:33:21 +0900</pubDate>
    </item>
    <item>
      <title>FastAPI란? Flask&amp;middot;Django 차이와 파이썬 백엔드 선택 기준</title>
      <link>https://notebase.tistory.com/entry/fastapi-flask-django-difference</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI의 개념과 Flask, Django와의 차이를 API 개발, 비동기 처리, 내장 기능, 학습 난이도 기준으로 비교합니다.&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;FastAPI는 Python으로 API 서버를 만들 때 자주 쓰이는 웹 프레임워크입니다. Flask보다 API 개발에 특화되어 있고, Django보다 가볍게 시작할 수 있다는 점이 핵심입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python 웹 개발을 찾아보면 보통 세 가지 이름을 많이 보게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI, Flask, Django.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 보면 셋 다 &amp;ldquo;Python으로 웹 서버 만드는 도구&amp;rdquo;처럼 보입니다. 맞는 말이긴 합니다. 다만 실제로는 지향점이 꽤 다릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI는 &lt;b&gt;API 서버를 빠르게 만들기 좋은 프레임워크&lt;/b&gt;에 가깝습니다.&lt;br /&gt;Flask는 &lt;b&gt;작고 유연한 마이크로 프레임워크&lt;/b&gt;입니다.&lt;br /&gt;Django는 &lt;b&gt;관리자 페이지, ORM, 인증 같은 기능까지 포함한 풀스택 웹 프레임워크&lt;/b&gt;에 가깝습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 차이를 모르고 시작하면 이런 상황이 생깁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 API 서버를 만들려고 Django를 골랐는데 설정이 무겁게 느껴질 수 있습니다. 반대로 회원 관리, 관리자 페이지, 게시판, 권한 기능까지 필요한 서비스를 Flask나 FastAPI로 만들면 직접 붙여야 할 게 많아집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;FastAPI란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI는 Python 타입 힌트를 기반으로 API를 만드는 웹 프레임워크입니다. 공식 문서에서도 FastAPI를 &amp;ldquo;표준 Python 타입 힌트를 기반으로 API를 만들기 위한 현대적이고 빠른 웹 프레임워크&amp;rdquo;라고 설명합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 예시는 이런 식입니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from fastapi import FastAPI

app = FastAPI()

@app.get(&quot;/hello&quot;)
def hello():
    return {&quot;message&quot;: &quot;Hello FastAPI&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;code&gt;/hello&lt;/code&gt; API를 만들 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 후 브라우저에서 &lt;code&gt;http://127.0.0.1:8000/docs&lt;/code&gt;에 접속하면 자동 생성된 API 문서를 바로 확인할 수 있습니다.&lt;br /&gt;&lt;code&gt;http://127.0.0.1:8000/redoc&lt;/code&gt;에서는 ReDoc 형식의 문서도 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI의 특징은 단순히 코드가 짧다는 데 있지 않습니다. 중요한 건 &lt;b&gt;요청 데이터 검증, 응답 문서화, API 문서 생성&lt;/b&gt;이 자연스럽게 연결된다는 점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 요청 바디를 받을 때 타입을 명확히 적어두면 FastAPI가 그 타입을 기준으로 데이터를 검증하고, OpenAPI 문서도 자동으로 만들어줍니다. FastAPI 공식 문서에 따르면 FastAPI는 OpenAPI 기반 문서를 만들고, &lt;code&gt;/docs&lt;/code&gt;와 &lt;code&gt;/redoc&lt;/code&gt; 형태의 대화형 문서 화면도 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API를 만들고 나서 프론트엔드 개발자나 앱 개발자에게 &amp;ldquo;이 API는 이런 값을 받고 이런 값을 반환한다&amp;rdquo;고 설명해야 할 때 이 차이가 꽤 큽니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;FastAPI의 핵심은 API 개발 경험이다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI가 주목받는 이유는 &amp;ldquo;빠르다&amp;rdquo; 하나로만 설명하면 부족합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 개발할 때 체감되는 장점은 다음 쪽에 가깝습니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;기준&lt;/th&gt;
&lt;th&gt;FastAPI 특징&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;주 용도&lt;/td&gt;
&lt;td&gt;REST API, 백엔드 API 서버, 마이크로서비스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;문법&lt;/td&gt;
&lt;td&gt;Python 타입 힌트 중심&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;문서화&lt;/td&gt;
&lt;td&gt;OpenAPI, Swagger UI, ReDoc 자동 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;데이터 검증&lt;/td&gt;
&lt;td&gt;Pydantic 기반 검증&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;비동기 처리&lt;/td&gt;
&lt;td&gt;&lt;code&gt;async&lt;/code&gt;, &lt;code&gt;await&lt;/code&gt; 사용에 적합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;기본 성격&lt;/td&gt;
&lt;td&gt;가볍지만 API 개발에 필요한 기능은 잘 갖춘 편&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;Python은 원래 동적 타입 언어라서 변수 타입을 강제하지 않습니다. 그런데 API 서버에서는 &amp;ldquo;이 값은 문자열이어야 한다&amp;rdquo;, &amp;ldquo;이 값은 숫자여야 한다&amp;rdquo;, &amp;ldquo;이 필드는 필수다&amp;rdquo; 같은 규칙이 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI는 이런 규칙을 Python 타입 힌트와 Pydantic 모델로 표현합니다. Pydantic은 타입 힌트를 기반으로 데이터 검증과 직렬화를 처리하는 Python 라이브러리입니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from pydantic import BaseModel
from fastapi import FastAPI

app = FastAPI()

class Post(BaseModel):
    title: str
    content: str
    published: bool = True

@app.post(&quot;/posts&quot;)
def create_post(post: Post):
    return post&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;code&gt;title&lt;/code&gt;과 &lt;code&gt;content&lt;/code&gt;는 문자열이어야 합니다. &lt;code&gt;published&lt;/code&gt;는 Boolean 값이고, 기본값은 &lt;code&gt;True&lt;/code&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 구조 덕분에 API 스펙이 코드 안에 자연스럽게 남습니다. 문서를 따로 작성하지 않아도 기본적인 API 문서가 자동으로 만들어지는 것도 이 흐름과 연결됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;FastAPI와 Flask 차이: API 서버 vs 마이크로 프레임워크&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flask는 Python 웹 프레임워크 중에서도 오래 쓰인 편이고, 구조가 단순합니다. 공식 문서에서도 Flask를 &amp;ldquo;가볍고 빠르게 시작할 수 있으며, 복잡한 애플리케이션으로 확장할 수 있는 WSGI 웹 애플리케이션 프레임워크&amp;rdquo;라고 설명합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flask의 기본 코드는 아주 짧습니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;from flask import Flask

app = Flask(__name__)

@app.route(&quot;/&quot;)
def home():
    return &quot;Hello Flask&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;Flask의 장점은 자유도입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스는 뭘 쓸지, 인증은 어떻게 붙일지, 폴더 구조는 어떻게 만들지 개발자가 직접 선택할 수 있습니다. 공식 문서에서도 Flask의 &amp;ldquo;micro&amp;rdquo;는 기능이 부족하다는 뜻이 아니라, 핵심을 단순하게 유지하고 확장 가능하게 만든다는 의미라고 설명합니다. Flask는 어떤 데이터베이스를 쓸지 같은 결정을 많이 강제하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 말하면, 프로젝트가 커질수록 선택해야 할 게 많아집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 로그인, 관리자 페이지, ORM, API 문서화, 데이터 검증 같은 기능은 Flask 기본 기능만으로 해결되지 않습니다. 필요한 확장 패키지를 골라 붙여야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI는 Flask보다 API 개발 쪽 기본 경험이 더 정리되어 있습니다. 요청 검증, 응답 모델, 자동 문서화가 처음부터 API 서버 개발 흐름에 맞춰 설계되어 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Flask가 더 나은 경우&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flask가 FastAPI보다 항상 부족한 건 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은 웹 페이지, 간단한 서버, Python 웹의 기본 구조를 배우는 용도라면 Flask가 더 편할 수 있습니다. 코드 흐름이 단순하고, 프레임워크가 많은 결정을 대신하지 않기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 비동기 API 서버를 중심으로 만들 계획이라면 FastAPI 쪽이 더 자연스럽습니다. Flask도 &lt;code&gt;async&lt;/code&gt; view를 지원하지만, 공식 문서에서는 Flask가 WSGI 애플리케이션이기 때문에 요청 하나를 처리할 때 워커 하나를 사용하고, async view는 스레드에서 이벤트 루프를 실행하는 방식이라고 설명합니다. Flask 설계 문서에서도 이 방식은 async-first ASGI 프레임워크와 비교해 성능 비용이 있을 수 있다고 안내합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 WSGI는 전통적인 Python 웹 서버 표준에 가깝고, ASGI는 비동기 처리까지 고려한 비교적 현대적인 표준이라고 보면 됩니다. 깊게 외울 필요는 없지만, FastAPI가 비동기 API 서버와 잘 맞는 이유를 이해할 때 중요한 배경입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;FastAPI와 Django 차이: 가벼운 API 서버 vs 풀스택 웹 프레임워크&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Django는 FastAPI나 Flask보다 더 큰 프레임워크입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Django 공식 사이트는 Django를 &amp;ldquo;빠른 개발과 깔끔하고 실용적인 디자인을 장려하는 고수준 Python 웹 프레임워크&amp;rdquo;라고 설명합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Django는 기본 제공 기능이 많습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적으로 이런 것들이 있습니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;기능&lt;/th&gt;
&lt;th&gt;Django&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ORM&lt;/td&gt;
&lt;td&gt;기본 제공&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;관리자 페이지&lt;/td&gt;
&lt;td&gt;기본 제공&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;인증 시스템&lt;/td&gt;
&lt;td&gt;기본 제공&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;템플릿&lt;/td&gt;
&lt;td&gt;기본 제공&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;폼 처리&lt;/td&gt;
&lt;td&gt;기본 제공&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;마이그레이션&lt;/td&gt;
&lt;td&gt;기본 제공&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API 개발&lt;/td&gt;
&lt;td&gt;Django REST Framework 등을 주로 함께 사용&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;Django의 강점은 &amp;ldquo;웹 서비스를 만드는 데 자주 필요한 기능&amp;rdquo;을 한 세트로 제공한다는 점입니다. 공식 문서에서도 Django 튜토리얼이 모델, 관리자 사이트, 뷰, 템플릿, 폼, 테스트, 정적 파일, 서드파티 패키지 추가 흐름으로 이어집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 관리자 페이지는 Django의 큰 장점입니다. Django admin은 기본 프로젝트 템플릿에서 활성화되어 있으며, 모델 데이터를 관리할 수 있는 관리자 인터페이스를 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회원가입, 로그인, 권한, 관리자 화면, 게시글 관리 같은 기능이 필요한 서비스라면 Django가 빠를 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 FastAPI는 이런 풀세트 기능을 기본으로 제공하지 않습니다. 필요한 데이터베이스 라이브러리, 인증 방식, 관리자 도구를 직접 선택해 구성하는 쪽에 가깝습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Django로도 API를 만들 수 있지 않나?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Django로 API 서버를 만들 때는 보통 Django REST Framework, 줄여서 DRF를 많이 사용합니다. DRF 공식 문서는 Django REST Framework를 &amp;ldquo;웹 API를 만들기 위한 강력하고 유연한 도구&amp;rdquo;라고 설명합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &amp;ldquo;API 서버 = 무조건 FastAPI&amp;rdquo;는 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 Django 프로젝트가 있고, 그 안에 사용자 모델, 관리자 페이지, 권한 체계, 데이터베이스 모델이 잘 잡혀 있다면 DRF로 API를 추가하는 쪽이 더 자연스러울 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 처음부터 프론트엔드와 백엔드를 분리하고, 백엔드는 JSON API만 제공하는 구조라면 FastAPI가 더 가볍게 느껴질 가능성이 큽니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;비동기 처리 기준으로 보면?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 처리는 FastAPI를 이야기할 때 자주 나오는 포인트입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI는 ASGI 기반의 현대적인 Python 웹 개발 흐름과 잘 맞습니다. &lt;code&gt;async&lt;/code&gt;, &lt;code&gt;await&lt;/code&gt;를 활용해 외부 API 호출, 네트워크 I/O, 비동기 DB 드라이버 같은 작업을 다루기 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Django도 비동기 지원이 있습니다. Django 6.0 공식 문서 기준으로 Django는 ASGI에서 실행할 경우 async view와 async-enabled request stack을 지원합니다. 다만 WSGI 환경에서도 async view가 동작할 수는 있지만 성능상 불리하고, 효율적인 장기 요청 처리에는 제한이 있다고 안내합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flask도 async view를 지원하지만, 앞에서 말한 것처럼 async-first 구조라기보다는 기존 WSGI 구조와의 호환성을 유지하면서 async를 지원하는 쪽에 가깝습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 이렇습니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;기준&lt;/th&gt;
&lt;th&gt;FastAPI&lt;/th&gt;
&lt;th&gt;Flask&lt;/th&gt;
&lt;th&gt;Django&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;비동기 중심 API 서버&lt;/td&gt;
&lt;td&gt;적합&lt;/td&gt;
&lt;td&gt;가능하지만 주력은 아님&lt;/td&gt;
&lt;td&gt;가능하지만 구조 확인 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;전통적인 웹 페이지&lt;/td&gt;
&lt;td&gt;가능하지만 주력은 아님&lt;/td&gt;
&lt;td&gt;적합&lt;/td&gt;
&lt;td&gt;매우 적합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;관리자 페이지&lt;/td&gt;
&lt;td&gt;직접 구성&lt;/td&gt;
&lt;td&gt;직접 구성&lt;/td&gt;
&lt;td&gt;기본 제공&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ORM&lt;/td&gt;
&lt;td&gt;직접 선택&lt;/td&gt;
&lt;td&gt;직접 선택&lt;/td&gt;
&lt;td&gt;기본 제공&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API 문서 자동화&lt;/td&gt;
&lt;td&gt;강점&lt;/td&gt;
&lt;td&gt;확장 필요&lt;/td&gt;
&lt;td&gt;DRF 등 추가 도구 활용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;학습 난이도&lt;/td&gt;
&lt;td&gt;중간&lt;/td&gt;
&lt;td&gt;낮은 편&lt;/td&gt;
&lt;td&gt;초반 설정은 많은 편&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;FastAPI를 선택하면 좋은 경우&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI는 이런 상황에서 잘 맞습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프론트엔드는 React, Vue, 앱 등으로 따로 만들고 백엔드는 API만 제공하는 경우&lt;/li&gt;
&lt;li&gt;JSON API 서버를 빠르게 만들고 싶은 경우&lt;/li&gt;
&lt;li&gt;요청/응답 데이터 검증이 중요한 경우&lt;/li&gt;
&lt;li&gt;Swagger UI 같은 API 문서가 자동으로 필요할 때&lt;/li&gt;
&lt;li&gt;외부 API 호출, 비동기 작업이 많은 서버를 만들 때&lt;/li&gt;
&lt;li&gt;AI 모델 서버, 데이터 처리 API, 마이크로서비스를 만들 때&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 AI나 데이터 관련 기능을 API로 감싸야 할 때 FastAPI가 자주 선택됩니다. 모델 추론 결과를 JSON으로 반환하거나, 내부 도구용 API를 만드는 식의 작업과 잘 맞습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 주의할 점도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI는 Django처럼 관리자 페이지, ORM, 인증, 권한, 템플릿을 한 번에 제공하지 않습니다. 물론 필요한 라이브러리를 붙이면 구현할 수 있지만, 그 선택과 구조 설계는 개발자 몫입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 하나는 버전입니다. 2026년 5월 기준 FastAPI 최신 버전은 0.136.1이며, 공식 문서에서는 FastAPI가 아직 0.x 버전대이고 변경 가능성이 있을 수 있으므로 사용하는 버전을 고정하라고 안내합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무 프로젝트라면 &lt;code&gt;requirements.txt&lt;/code&gt;나 &lt;code&gt;pyproject.toml&lt;/code&gt;에서 버전을 명확히 고정하는 편이 안전합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Flask를 선택하면 좋은 경우&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flask는 작게 시작하기 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 웹 페이지, 테스트용 서버, 사내에서 잠깐 쓰는 도구, Python 웹의 기본 개념을 배우는 목적이라면 Flask가 부담이 적습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flask는 많은 걸 강제하지 않습니다. 이 점이 장점이기도 하고 단점이기도 합니다.&lt;/p&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;예를 들어 데이터베이스 ORM으로 SQLAlchemy를 쓸지, 인증은 어떤 확장을 쓸지, 프로젝트 구조는 어떻게 나눌지, API 문서는 어떻게 만들지 등을 직접 정해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Flask는 &amp;ldquo;작고 단순한 것부터 직접 조립해보고 싶은 경우&amp;rdquo;에 잘 맞습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Django를 선택하면 좋은 경우&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Django는 서비스 전체를 빠르게 만들고 싶을 때 강합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 이런 프로젝트라면 Django가 잘 맞습니다.&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;데이터베이스 CRUD가 많은 서비스&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;Django는 기본 제공 기능이 많기 때문에 초반에는 배울 게 많아 보입니다. 하지만 일정 규모 이상에서는 오히려 편해질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 관리자 페이지가 필요한 프로젝트라면 차이가 큽니다. FastAPI나 Flask에서는 관리자 기능을 직접 만들거나 별도 도구를 붙여야 하지만, Django는 admin 기능이 기본 생태계 안에 들어와 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 2026년 기준으로 Django 최신 버전인 6.0 계열은 Python 3.12, 3.13, 3.14를 지원합니다. 기존 프로젝트가 Python 3.10이나 3.11에 머물러 있다면 Django 6.0으로 올릴 때 호환성을 먼저 확인해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;셋 중 하나를 고르는 기준&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 너무 어렵게 생각할 필요는 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;내가 만들려는 게 무엇인가?&amp;rdquo;를 먼저 보면 됩니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;만들려는 것&lt;/th&gt;
&lt;th&gt;추천&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;간단한 Python 웹 서버&lt;/td&gt;
&lt;td&gt;Flask&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JSON API 서버&lt;/td&gt;
&lt;td&gt;FastAPI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI 모델 호출 API&lt;/td&gt;
&lt;td&gt;FastAPI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;관리자 페이지가 필요한 웹 서비스&lt;/td&gt;
&lt;td&gt;Django&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;회원&amp;middot;권한&amp;middot;게시판 중심 서비스&lt;/td&gt;
&lt;td&gt;Django&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;작은 실험용 프로젝트&lt;/td&gt;
&lt;td&gt;Flask 또는 FastAPI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;프론트엔드 분리형 백엔드&lt;/td&gt;
&lt;td&gt;FastAPI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;기존 Django 서비스에 API 추가&lt;/td&gt;
&lt;td&gt;Django + DRF&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;웹 서비스 성능은 프레임워크 하나로 결정되지 않습니다. 데이터베이스 쿼리, 캐시, 배포 구조, 네트워크, ORM 사용 방식, 서버 설정이 더 크게 작용할 때가 많습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI가 API 개발에 유리한 건 맞지만, 모든 상황에서 무조건 FastAPI가 답은 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;입문자는 무엇부터 배우는 게 좋을까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 개발 자체가 처음이라면 Flask로 HTTP 요청과 응답의 기본을 가볍게 익히는 것도 괜찮습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 목표가 &amp;ldquo;실제 API 서버를 만들고 싶다&amp;rdquo;라면 FastAPI로 바로 시작해도 됩니다. 타입 힌트, 요청 검증, 자동 문서화 흐름을 처음부터 익힐 수 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Django는 웹 서비스 전체 구조를 배우고 싶을 때 좋습니다. 모델, 뷰, 템플릿, 관리자 페이지, 인증, 권한 같은 요소를 한 프레임워크 안에서 경험할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 Django를 처음 배울 때는 &amp;ldquo;왜 이렇게 파일이 많지?&amp;rdquo;라는 느낌을 받을 수 있습니다. 그건 Django가 많은 기능을 미리 갖춘 프레임워크이기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;FastAPI, Flask, Django 차이 정리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;짧게 정리하면 이렇습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI는 &lt;b&gt;API 서버 중심&lt;/b&gt;입니다.&lt;br /&gt;Flask는 &lt;b&gt;작고 유연한 마이크로 프레임워크&lt;/b&gt;입니다.&lt;br /&gt;Django는 &lt;b&gt;기능이 많이 포함된 웹 서비스 중심&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI는 프론트엔드와 백엔드를 분리한 구조, 모바일 앱 API, AI API 서버, 마이크로서비스에 잘 맞습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flask는 작게 시작하고 직접 조립하면서 배우기 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Django는 회원, 관리자, 데이터베이스, 권한, 백오피스가 필요한 서비스에 강합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로는 셋 중 하나가 절대적으로 좋은 게 아닙니다. 프로젝트 성격이 다를 뿐입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API만 만든다면 FastAPI.&lt;br /&gt;작은 웹 서버나 학습용이면 Flask.&lt;br /&gt;서비스 전체를 빠르게 구성해야 한다면 Django.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 기준으로 보면 선택이 훨씬 쉬워집니다.&lt;/p&gt;</description>
      <category>개발/FastAPI</category>
      <category>Python</category>
      <category>개발도구</category>
      <category>코딩입문</category>
      <category>파이썬</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/44</guid>
      <comments>https://notebase.tistory.com/entry/fastapi-flask-django-difference#entry44comment</comments>
      <pubDate>Sat, 23 May 2026 20:03:16 +0900</pubDate>
    </item>
    <item>
      <title>바이브 코딩이란? AI가 만든 코드를 그대로 쓰면 안 되는 이유</title>
      <link>https://notebase.tistory.com/entry/vibe-coding-ai-code-review-risk</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;바이브 코딩의 뜻과 최근 AI 코딩 흐름을 정리하고, AI가 만든 코드를 그대로 쓰면 안 되는 이유를 코드 품질, 보안, 유지보수 관점에서 설명합니다.&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;바이브 코딩은 AI에게 자연어로 원하는 기능을 설명하고, 생성된 코드를 바탕으로 빠르게 개발하는 방식이다. 코드 한 줄을 직접 고민하기보다 &amp;ldquo;이런 기능 만들어줘&amp;rdquo;에 가깝게 개발 흐름을 가져간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름 그대로 세부 구현을 한 줄씩 통제하기보다, 전체적인 방향을 잡고 AI가 코드를 만들어가게 두는 방식에 가깝다. 개발자가 모든 코드를 직접 작성하는 대신, 구현의 흐름과 결과물을 조정하는 쪽으로 역할이 이동하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전에는 개발자가 문법, 라이브러리, 구조를 직접 잡고 AI는 보조 역할을 했다. 지금은 흐름이 조금 달라졌다. Cursor, Claude Code, GitHub Copilot, ChatGPT 같은 도구를 쓰면 파일을 읽고, 코드를 고치고, 테스트 코드까지 제안한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 바이브 코딩은 단순히 &amp;ldquo;AI에게 코드 물어보기&amp;rdquo;가 아니다. 개발자가 구현 방향을 말하면 AI가 상당한 양의 코드를 만들어내고, 사람은 그 결과를 보고 방향을 조정하는 방식에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식 자체가 나쁜 것은 아니다. 문제는 AI가 만든 코드가 그럴듯해 보인다는 데 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;바이브 코딩은 왜 빠르게 퍼졌을까&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바이브 코딩이 주목받은 이유는 명확하다. 개발 속도가 빨라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 CRUD, 관리자 페이지, API 연동, 스크립트 자동화, 테스트 코드 초안 같은 작업은 AI가 꽤 잘 처리한다. 특히 이미 구조가 잡힌 프로젝트에서는 &amp;ldquo;이 패턴을 따라 기능 하나 추가해줘&amp;rdquo;라고 요청했을 때 결과물이 생각보다 빨리 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자가 문서를 찾고, 예제 코드를 뒤지고, 보일러플레이트를 작성하는 시간을 줄일 수 있다. 이건 분명한 장점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 여기서 착각하기 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 코드를 빠르게 만든다는 말과, 그 코드가 제품에 바로 들어가도 된다는 말은 다르다. Stack Overflow 2025 개발자 설문에서도 AI 도구 출력의 정확성을 신뢰하지 않는 개발자가 신뢰하는 개발자보다 더 많았다. 즉, 개발자들은 AI를 쓰고 있지만 동시에 의심하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로는 &amp;ldquo;작성 시간&amp;rdquo;이 줄어든 만큼 &amp;ldquo;검증 시간&amp;rdquo;이 생긴다. 이 검증을 건너뛰면 바이브 코딩은 생산성 도구가 아니라 부채 생성기가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;AI 코드는 왜 위험할 수 있을까&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 만든 코드는 대체로 그럴듯하다. 함수 이름도 자연스럽고, 주석도 붙어 있고, 에러 처리까지 있는 것처럼 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 코드는 겉모습보다 맥락이 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 로그인 기능을 만든다고 해보자. AI는 빠르게 인증 로직을 만들어줄 수 있다. 하지만 실제 서비스에서는 이런 질문이 따라온다.&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;refresh token은 어떤 조건에서 재발급할 것인가&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;AI가 이런 맥락을 전부 알고 코드를 작성하는 경우는 드물다. 프롬프트에 명시하지 않은 조건은 조용히 빠진다. 프로젝트 내부 규칙을 충분히 제공하지 않았다면 일반적인 예제 코드에 가까운 결과를 낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점은, AI가 틀린 코드를 만들 때도 대체로 자신감 있게 만든다는 것이다. 컴파일이 되고, 화면이 뜨고, 기본 케이스가 통과하면 문제 없어 보인다. 하지만 운영 환경에서는 예외 케이스가 더 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&amp;ldquo;동작하는 코드&amp;rdquo;와 &amp;ldquo;운영 가능한 코드&amp;rdquo;는 다르다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바이브 코딩에서 가장 많이 생기는 착각은 이것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;일단 실행되니까 괜찮다.&amp;rdquo;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발에서는 실행되는 코드가 출발점이지, 도착점이 아니다. 운영 가능한 코드는 더 많은 조건을 만족해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러가 났을 때 복구 가능한가.&lt;br /&gt;로그가 추적 가능하게 남는가.&lt;br /&gt;권한 체크가 빠지지 않았는가.&lt;br /&gt;데이터가 중복 저장되지 않는가.&lt;br /&gt;장애 상황에서 재시도 로직이 문제를 키우지 않는가.&lt;br /&gt;나중에 다른 개발자가 수정할 수 있는 구조인가.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 만든 코드는 이런 부분에서 약해질 수 있다. 특히 비즈니스 규칙이 복잡한 서비스일수록 더 그렇다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 결제 취소 로직을 AI에게 맡겼다고 해보자. 표면적으로는 &lt;code&gt;cancelPayment()&lt;/code&gt; 함수 하나면 끝난다. 하지만 실제로는 PG사 취소, 내부 주문 상태 변경, 쿠폰 복원, 포인트 환불, 재고 복구, 알림 발송, 중복 요청 방지까지 연결된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 중 하나만 빠져도 서비스 장애가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI는 코드를 만들 수 있지만, 그 코드가 조직의 정책과 데이터 흐름을 정확히 이해하고 있는지는 별개의 문제다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;AI가 만든 코드를 그대로 쓰면 안 되는 이유&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 요구사항을 과하게 단순화한다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI는 사용자가 말한 내용을 바탕으로 코드를 만든다. 문제는 사용자가 요구사항을 완벽하게 설명하지 않는다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람 개발자라면 &amp;ldquo;이 부분은 정책이 더 필요해 보이는데요&amp;rdquo;라고 되묻는다. 반면 AI는 많은 경우 일단 구현한다. 빠르게 결과를 보여주는 쪽으로 움직인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 AI 코드는 요구사항의 빈틈을 조용히 추정으로 채운다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 추정이 맞으면 편하다. 틀리면 위험하다. 더 큰 문제는 어떤 부분이 추정인지 코드만 보고 바로 알아차리기 어렵다는 점이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 보안 기본값이 항상 안전하지 않다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 제안하는 코드는 인터넷에 공개된 예제, 일반적인 패턴, 문서의 샘플 코드와 비슷한 형태가 많다. 예제 코드는 이해를 돕기 위한 코드이지, 그대로 운영 서비스에 넣으라고 만든 코드가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증, 권한, 입력값 검증, 파일 업로드, SQL 쿼리, 외부 API 호출 같은 부분은 특히 조심해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OWASP는 LLM 기반 애플리케이션에서 프롬프트 인젝션, 안전하지 않은 출력 처리, 공급망 취약점 등을 주요 위험으로 분류한다. AI 코딩 도구를 쓸 때도 이 문제는 그대로 이어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 AI가 만든 코드가 사용자 입력값을 그대로 쿼리에 넣거나, 업로드 파일의 확장자만 검사하거나, 관리자 권한 체크를 프론트엔드에서만 처리한다면 겉으로는 기능이 돌아가도 보안상 위험하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 하나 조심할 부분은 &lt;b&gt;AI 패키지 할루시네이션(AI Package Hallucination)&lt;/b&gt;이다. AI가 실제로 존재하지 않는 오픈소스 패키지를 그럴듯하게 추천하고, 공격자가 그 이름의 악성 패키지를 선점해 배포하면 공급망 공격으로 이어질 수 있다. 이런 흐름은 &lt;b&gt;슬롭스쿼팅(slopsquatting)&lt;/b&gt;이라고도 불린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제는 생각보다 현실적이다. 개발자는 AI가 추천한 패키지를 &amp;ldquo;문서에 있겠지&amp;rdquo;라고 넘기기 쉽고, 자동완성 도구나 에이전트형 도구가 설치 명령까지 제안하면 검증 없이 의존성이 추가될 수 있다. 특히 npm, PyPI처럼 패키지 설치가 쉬운 생태계에서는 패키지명 확인이 더 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보안은 &amp;ldquo;나중에 한 번 점검&amp;rdquo;으로 해결하기 어렵다. 처음 구조를 잡을 때부터 들어가야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 프로젝트 컨벤션을 어긴다&lt;/b&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;폴더 구조, 네이밍 규칙, 에러 응답 형식, 로깅 방식, 트랜잭션 처리 기준, 테스트 작성 방식, API 응답 포맷, 상태 관리 방식 같은 것들이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI는 이런 규칙을 모르면 자기 방식대로 코드를 만든다. 당장은 기능이 추가되지만, 프로젝트 전체 일관성은 깨진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 한두 번은 괜찮아 보인다. 하지만 비슷한 방식으로 AI 코드를 계속 붙이면 코드베이스 안에 서로 다른 스타일이 섞인다. 이때부터 유지보수 비용이 올라간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코드는 새 코드를 만드는 데 강하지만, 기존 코드의 맥락을 정확히 존중하는 데는 사람의 검토가 필요하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. 테스트가 빈약하거나 엉뚱할 수 있다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI는 테스트 코드도 만들어준다. 이 점은 유용하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 AI가 만든 테스트가 정말 중요한 케이스를 검증하는지는 따로 봐야 한다. 흔히 발생하는 문제는 &amp;ldquo;정상 케이스만 테스트하는 코드&amp;rdquo;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 회원가입 기능이라면 정상 가입만 테스트하는 것이 아니라 이미 가입된 이메일, 잘못된 이메일 형식, 비밀번호 정책 위반, 인증 메일 발송 실패, DB 저장 실패 같은 케이스도 봐야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 만든 테스트가 많다고 해서 안전한 것은 아니다. 테스트 개수보다 중요한 건 어떤 위험을 검증하느냐다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5. 리뷰할 코드 양이 늘어난다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI를 쓰면 코드 생산량이 늘어난다. 이 자체는 장점이다. 하지만 팀에서 리뷰할 수 있는 양은 무한하지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;짧은 시간에 큰 PR이 만들어지고, 그 안에 AI가 생성한 파일이 많이 섞이면 리뷰어는 피로해진다. 사람이 직접 쓴 코드보다 의도를 파악하기 어려운 경우도 있다. 작성자는 &amp;ldquo;AI가 만든 코드라 나도 자세히는 모른다&amp;rdquo;는 상태가 될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 위험한 신호다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드의 최종 책임은 AI가 아니라 커밋한 사람에게 있다. 내가 설명하지 못하는 코드는 내가 소유한 코드라고 보기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;바이브 코딩을 버려야 할까&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럴 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바이브 코딩은 잘 쓰면 강력하다. 특히 다음 작업에서는 꽤 유용하다.&lt;/p&gt;
&lt;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;li&gt;리팩터링 방향 탐색&lt;/li&gt;
&lt;li&gt;문서 기반 API 사용 예제 작성&lt;/li&gt;
&lt;li&gt;작은 스크립트 자동화&lt;/li&gt;
&lt;li&gt;프로토타입 제작&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 &amp;ldquo;AI가 만들었으니 빠르게 배포하자&amp;rdquo;는 태도다. 바이브 코딩은 개발자를 없애는 방식이 아니라, 개발자의 검토 지점을 바꾸는 방식에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전에는 직접 코드를 쓰는 능력이 중요했다. 지금은 코드를 읽고, 의심하고, 구조를 판단하고, 위험을 찾아내는 능력이 더 중요해지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Google Cloud의 2025 DORA 보고서 소개 자료도 AI 도입이 넓게 확산됐지만, 소프트웨어 전달 안정성과는 여전히 부정적 관계가 있다고 설명한다. AI를 쓴다고 자동으로 더 좋은 개발 조직이 되는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;AI 코드 검토할 때 봐야 할 기준&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 만든 코드를 받았다면 바로 붙여넣기보다 아래 기준으로 보는 게 낫다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;내가 설명할 수 있는 코드인가&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 볼 것은 이해 여부다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 함수가 왜 필요한지, 어떤 입력을 받고, 어떤 상태를 바꾸며, 실패했을 때 어떻게 되는지 설명할 수 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설명하지 못하는 코드는 아직 내 코드가 아니다. 그 상태로 커밋하면 나중에 장애가 났을 때 원인 분석도 어려워진다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;기존 구조와 맞는가&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 새 패턴을 만들고 있지는 않은지 봐야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 프로젝트에서 에러를 &lt;code&gt;AppError&lt;/code&gt;로 감싸고 있는데 AI가 갑자기 일반 &lt;code&gt;Error&lt;/code&gt;를 던진다면 문제가 된다. API 응답 형식이 정해져 있는데 AI가 다른 포맷을 만들었다면 나중에 프론트엔드나 클라이언트 앱에서 깨질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드가 맞는지만 보지 말고, 프로젝트 안에서 자연스러운지도 봐야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;실패 케이스가 처리되어 있는가&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상 흐름만 보면 대부분의 코드는 그럴듯하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 봐야 할 것은 실패 흐름이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 API가 timeout이면 어떻게 되는가.&lt;br /&gt;DB 저장 중 예외가 나면 롤백되는가.&lt;br /&gt;중복 요청이 들어오면 한 번만 처리되는가.&lt;br /&gt;권한 없는 사용자가 요청하면 어디서 막히는가.&lt;br /&gt;로그에 개인정보가 남지는 않는가.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 질문에 답하지 못하면 아직 운영 코드로 보기 어렵다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;테스트가 위험을 검증하는가&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 만든 테스트는 초안으로는 좋다. 하지만 테스트가 실제 위험을 잡는지는 사람이 판단해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 &amp;ldquo;성공한다&amp;rdquo;를 확인하는 테스트보다 &amp;ldquo;실패해야 할 때 실패하는지&amp;rdquo;를 보는 테스트가 더 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;권한이 없으면 실패해야 한다.&lt;br /&gt;잘못된 입력이면 저장되지 않아야 한다.&lt;br /&gt;중복 요청이면 두 번 처리되지 않아야 한다.&lt;br /&gt;외부 연동 실패 시 내부 상태가 어긋나지 않아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 테스트가 있어야 AI 코드도 어느 정도 신뢰할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;실무에서는 이렇게 쓰는 게 낫다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바이브 코딩을 실무에 쓸 때는 AI에게 &amp;ldquo;전체를 알아서 만들어줘&amp;rdquo;보다 작업 범위를 좁히는 편이 낫다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 이렇게 요청하는 것이다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;기존 UserService 패턴을 유지해서 비밀번호 변경 기능을 추가해줘.
단, 아래 조건을 지켜줘.

- 현재 비밀번호 검증 필요
- 새 비밀번호는 기존 비밀번호와 같으면 안 됨
- 실패 응답은 기존 AppError 형식 사용
- DB 업데이트는 transaction 안에서 처리
- 테스트는 성공 케이스보다 실패 케이스 중심으로 작성&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;이렇게 주면 AI가 추정해야 하는 부분이 줄어든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 아래처럼 요청하면 위험하다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&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;너무 짧은 프롬프트는 빠른 결과를 주지만, 그만큼 많은 결정을 AI에게 넘긴다. 바이브 코딩의 핵심은 프롬프트를 길게 쓰는 것이 아니라, AI가 마음대로 결정하면 안 되는 부분을 명확히 고정하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;개인과 팀은 무엇을 다르게 봐야 할까&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인 개발자라면 AI가 만든 코드를 붙여넣기 전에 &amp;ldquo;실패 케이스와 보안상 위험을 찾아줘&amp;rdquo;라고 한 번 더 물어보는 습관이 필요하다. AI에게 코드를 만들게 하는 것만큼, AI에게 자기 코드의 약점을 지적하게 하는 것도 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀 단위로 쓴다면 더 중요하다. AI 생성 코드에 대한 PR 기준, 의존성 검증 방식, 테스트 요구사항을 팀 규칙으로 정해두지 않으면 생산성 향상이 그대로 코드 부채로 바뀔 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;팀에서 바로 적용해 볼 수 있는 AI 코드 리뷰 규칙&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;의존성 검증:&lt;/b&gt; AI가 추가하거나 제안한 패키지는 공식 저장소와 다운로드 이력을 확인한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;핵심 로직 리뷰:&lt;/b&gt; 인증, 결제, 권한, 개인정보 처리 코드는 AI가 작성했더라도 사람이 직접 한 줄씩 확인한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;실패 중심 테스트:&lt;/b&gt; AI가 작성한 테스트는 정상 작동보다 예외 케이스를 제대로 검증하는지 우선 확인한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;PR 단위 쪼개기:&lt;/b&gt; 큰 PR 하나보다 작은 기능 단위로 나눠 리뷰한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;코드 소유권 확인:&lt;/b&gt; 작성자가 설명하지 못하는 코드는 병합하지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정도만 정해도 바이브 코딩의 위험은 꽤 줄어든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;바이브 코딩은 개발자의 실력을 더 중요하게 만든다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;겉으로 보면 AI가 개발자의 일을 대신하는 것처럼 보인다. 하지만 실제로는 개발자의 판단이 더 중요해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 코드를 많이 만들수록 사람은 더 많은 코드를 검토해야 한다.&lt;br /&gt;AI가 빠르게 기능을 붙일수록 사람은 구조가 망가지지 않는지 봐야 한다.&lt;br /&gt;AI가 그럴듯한 답을 낼수록 사람은 틀린 부분을 더 잘 찾아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 바이브 코딩 시대의 개발자는 단순히 코드를 많이 치는 사람이 아니라, 코드의 방향과 품질을 판단하는 사람이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 만든 코드를 그대로 쓰면 안 되는 이유도 여기에 있다. AI의 능력이 부족해서가 아니다. 좋은 소프트웨어는 잘 돌아가는 코드 조각을 이어 붙인 것만으로 완성되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 정책, 사용자 데이터, 보안, 장애 대응, 팀의 유지보수 방식까지 함께 맞아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바이브 코딩은 빠른 출발점으로 쓰기 좋다. 다만 최종 판단은 사람이 해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI에게 코드를 맡길 수는 있다.&lt;br /&gt;하지만 책임까지 맡길 수는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI/AI 코딩</category>
      <category>AI코드리뷰</category>
      <category>AI코딩</category>
      <category>claudecode</category>
      <category>CURSOR</category>
      <category>githubcopilot</category>
      <category>개발자도구</category>
      <category>바이브코딩</category>
      <category>코드품질</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/43</guid>
      <comments>https://notebase.tistory.com/entry/vibe-coding-ai-code-review-risk#entry43comment</comments>
      <pubDate>Sat, 23 May 2026 18:49:17 +0900</pubDate>
    </item>
    <item>
      <title>Codex란? ChatGPT와 다른 개발용 AI 도구 이해하기</title>
      <link>https://notebase.tistory.com/entry/what-is-codex-chatgpt-difference</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Codex가 무엇인지, ChatGPT와 어떤 점이 다른지 개발 작업 흐름 기준으로 정리했습니다. Codex CLI, 코드 수정, 리뷰, 테스트, 보안 주의사항까지 개발자가 알아야 할 핵심을 설명합니다.&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;Codex는 OpenAI가 제공하는 &lt;b&gt;개발용 AI 코딩 에이전트&lt;/b&gt;다. ChatGPT가 코딩 질문에 답하고 개념을 설명해주는 도구에 가깝다면, Codex는 실제 프로젝트 안에서 코드를 읽고, 수정하고, 실행하는 흐름까지 도와주는 도구에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 들으면 &amp;ldquo;그냥 코딩 잘하는 ChatGPT 아닌가?&amp;rdquo; 싶을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;틀린 말은 아니지만, 정확히는 조금 다르다.&lt;br /&gt;ChatGPT가 대화창에서 개발을 도와주는 방식이라면, Codex는 개발 환경 안으로 들어와서 작업을 함께 처리하는 쪽에 더 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 말하는 Codex는 과거 OpenAI API에서 제공되던 단순 코드 생성 모델을 뜻하는 것이 아니다.&lt;br /&gt;2026년 5월 기준으로는 &lt;b&gt;Codex CLI, Codex app, IDE 연동처럼 개발 환경 안에서 동작하는 AI 코딩 에이전트 환경&lt;/b&gt;을 기준으로 설명한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 차이는 &amp;ldquo;코딩을 더 잘하느냐&amp;rdquo;가 아니라, &lt;b&gt;어디에서 어떤 방식으로 개발을 돕느냐&lt;/b&gt;에 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Codex는 무엇인가&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex는 개발자가 요청한 작업을 바탕으로 코드 작성, 수정, 리뷰, 테스트 같은 일을 도와주는 AI 코딩 에이전트다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenAI 공식 문서에서도 Codex는 소프트웨어 개발을 위한 코딩 에이전트라고 설명한다. 코드 작성, 낯선 코드베이스 이해, 코드 리뷰 같은 작업을 도울 수 있는 도구에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 이런 작업을 맡길 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 코드 구조 파악&lt;/li&gt;
&lt;li&gt;새로운 기능 구현&lt;/li&gt;
&lt;li&gt;버그 원인 분석&lt;/li&gt;
&lt;li&gt;테스트 코드 작성&lt;/li&gt;
&lt;li&gt;리팩터링&lt;/li&gt;
&lt;li&gt;Pull Request 리뷰&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;여기서 중요한 건 Codex가 단순히 코드 조각만 보여주는 도구가 아니라는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 ChatGPT 사용 방식은 이렇다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&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;그러면 ChatGPT는 원인을 설명하고, 예시 코드를 제안해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 Codex에는 이런 식으로 요청할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&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;br /&gt;프로젝트 구조를 보고, 관련 파일을 찾고, 실제 수정이 필요한 부분을 판단해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex는 이런 작업 흐름에 맞춰 만들어진 개발용 AI 도구라고 보면 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;ChatGPT와 Codex의 가장 큰 차이&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChatGPT도 코딩을 잘한다.&lt;br /&gt;함수 하나를 작성하거나, 에러 메시지를 해석하거나, 새로운 기술 개념을 이해하는 용도로는 충분히 유용하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 &lt;b&gt;실제 프로젝트 작업&lt;/b&gt;으로 들어가면 상황이 달라진다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;ChatGPT&lt;/th&gt;
&lt;th&gt;Codex&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;기본 목적&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;범용 AI 대화와 개발 상담&lt;/td&gt;
&lt;td&gt;개발 작업 수행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;사용 방식&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;대화창에 질문하고 답변 받기&lt;/td&gt;
&lt;td&gt;프로젝트 안에서 작업 맡기기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;코드 이해&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;사용자가 붙여넣은 코드 중심&lt;/td&gt;
&lt;td&gt;파일 구조와 코드베이스를 함께 참고&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;코드 수정&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;수정 방향과 예시 코드 제안&lt;/td&gt;
&lt;td&gt;실제 파일을 읽고 변경 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;명령 실행&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;일반 대화창에서는 설명&amp;middot;제안 중심&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Codex CLI에서 터미널 기반 작업 가능&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;적합한 상황&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;개념 설명, 코드 질문, 설계 상담&lt;/td&gt;
&lt;td&gt;기능 구현, 버그 수정, PR 리뷰, 테스트 작성&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;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;한 줄로 보면&lt;/b&gt;&lt;br /&gt;ChatGPT는 개발 방향을 정리할 때 편하고, Codex는 실제 프로젝트 안에서 코드를 고치고 확인하는 작업에 더 잘 맞는다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 이런 질문은 ChatGPT에 더 잘 맞는다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;React에서 useEffect가 두 번 실행되는 이유가 뭐야?&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;br /&gt;굳이 프로젝트 전체를 볼 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 이런 요청은 Codex에 더 잘 맞는다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&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;br /&gt;폼 컴포넌트가 어디 있는지, 유효성 검사 방식이 기존에 어떻게 되어 있는지, 테스트는 어떤 방식으로 작성되어 있는지 확인해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 맥락을 따라가면서 작업하는 데 Codex가 더 적합하다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Codex CLI는 무엇인가&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex는 여러 방식으로 사용할 수 있다.&lt;br /&gt;대표적으로는 Codex CLI, Codex app, IDE 확장, Codex web 같은 형태가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그중 개발자에게 특히 익숙한 방식은 &lt;b&gt;Codex CLI&lt;/b&gt;다.&lt;br /&gt;Codex CLI는 터미널에서 실행하는 방식이라, 기존 개발 흐름을 크게 바꾸지 않고도 AI에게 코드 수정이나 테스트 실행을 맡길 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널에서 Codex를 실행하고 작업을 요청하는 흐름은 아래처럼 이해하면 된다.&lt;/p&gt;
&lt;pre class=&quot;shell&quot;&gt;&lt;code&gt;$ codex
&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;그러면 Codex는 단순히 &amp;ldquo;이렇게 하면 됩니다&amp;rdquo;라고 답하는 데서 끝나지 않는다.&lt;br /&gt;선택한 디렉터리 안의 프로젝트 파일을 살펴보고, 수정할 부분을 찾고, 필요한 경우 명령을 실행하면서 작업을 이어갈 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 차이가 꽤 크다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChatGPT는 보통 개발자가 가져온 정보를 바탕으로 답변한다.&lt;br /&gt;Codex CLI는 개발자가 열어둔 프로젝트 안에서 필요한 정보를 직접 찾아가며 작업한다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;ChatGPT로도 코딩할 수 있는데 왜 Codex가 필요할까&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은 질문은 ChatGPT가 더 빠르고 편하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 이런 질문은 ChatGPT만으로 충분하다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;파이썬에서 리스트 컴프리헨션이 뭐야?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;JWT와 세션 인증 차이를 알려줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;이 SQL 쿼리가 느린 이유를 설명해줘.&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;br /&gt;프로젝트 전체 맥락이 없어도 답변할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제 개발에서는 코드가 여러 파일에 나뉘어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인 기능 하나만 봐도 API, 라우터, 상태 관리, UI 컴포넌트, 테스트 코드가 얽혀 있을 수 있다.&lt;br /&gt;이걸 전부 ChatGPT에 복사해서 붙여넣는 방식은 금방 불편해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 어떤 파일을 보여줘야 하는지도 개발자가 직접 판단해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex는 이 부분에서 장점이 있다.&lt;br /&gt;프로젝트 구조를 보고, 관련 파일을 찾고, 기존 코드 스타일에 맞춰 작업을 이어갈 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로는 &amp;ldquo;AI가 코드를 잘 짜는가&amp;rdquo;만큼이나 &lt;b&gt;내 프로젝트 맥락을 잘 따라오는가&lt;/b&gt;가 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무리 깔끔한 코드라도 기존 구조와 맞지 않으면 유지보수하기 어렵다.&lt;br /&gt;Codex 같은 개발용 AI 도구는 이 문제를 줄이는 방향으로 설계되어 있다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Codex app과 IDE 연동은 언제 쓸까&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex CLI가 터미널 중심이라면, Codex app은 여러 작업을 한 곳에서 관리하는 쪽에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenAI 공식 문서 기준으로 Codex app은 여러 Codex 작업 스레드를 병렬로 다루고, worktree와 Git 기능을 함께 활용할 수 있는 데스크톱 경험으로 소개된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 단순히 터미널 한 곳에서 작업하는 것보다 여러 작업을 나눠서 관리하고 싶을 때 Codex app이 더 편할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 이런 상황이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;버그 수정 작업과 리팩터링 작업을 나눠서 진행할 때&lt;/li&gt;
&lt;li&gt;여러 에이전트 작업 결과를 비교하고 싶을 때&lt;/li&gt;
&lt;li&gt;변경된 코드를 리뷰하고 Git 흐름과 함께 보고 싶을 때&lt;/li&gt;
&lt;li&gt;프로젝트별 작업 스레드를 따로 관리하고 싶을 때&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IDE 연동은 조금 더 익숙한 개발 환경 안에서 Codex를 쓰고 싶을 때 유용하다.&lt;br /&gt;VS Code 같은 에디터 안에서 코드와 AI 작업 흐름을 함께 보고 싶다면 IDE 연동 방식이 더 자연스러울 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 써본다면 Codex CLI로 작은 작업부터 시작하는 편이 부담이 적다.&lt;br /&gt;이후 작업이 많아지고 여러 흐름을 나눠 관리해야 한다면 Codex app이나 IDE 연동을 살펴보면 된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Codex는 코드 리뷰에도 활용할 수 있다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex는 코드 작성뿐 아니라 리뷰에도 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Pull Request를 올린 뒤, 변경된 코드에서 문제가 될 만한 부분을 확인하는 식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혼자 개발하다 보면 놓치기 쉬운 부분이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예외 처리가 빠진 부분&lt;/li&gt;
&lt;li&gt;테스트가 없는 변경&lt;/li&gt;
&lt;li&gt;인증이나 권한 처리 문제&lt;/li&gt;
&lt;li&gt;불필요하게 복잡한 코드&lt;/li&gt;
&lt;li&gt;기존 코드 스타일과 맞지 않는 부분&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex 리뷰를 활용하면 최소한 한 번 더 검토하는 단계를 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 AI 리뷰를 사람 리뷰의 완전한 대체로 보면 안 된다.&lt;br /&gt;특히 보안, 결제, 개인정보, 권한 처리처럼 민감한 부분은 반드시 사람이 직접 확인해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex는 검토를 도와주는 도구이지, 최종 책임을 대신 져주는 도구는 아니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Codex를 쓸 때 주의할 점&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex는 프로젝트 파일을 읽고 수정할 수 있다.&lt;br /&gt;이 점이 편리하지만, 동시에 조심해야 할 부분이기도 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 봐야 할 것은 접근 범위다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex CLI를 사용할 때는 실행 위치가 중요하다.&lt;br /&gt;현재 열어둔 디렉터리를 기준으로 파일을 읽고 수정할 수 있기 때문에, 민감한 파일이 있는 폴더에서는 접근 범위를 먼저 확인하는 편이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 이런 파일이다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;.env
.env.local
credentials.json
private-key.pem
service-account.json&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 오해하기 쉬운 부분이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;.gitignore&lt;/code&gt;에 넣었다고 해서 AI 도구가 해당 파일을 못 읽는 것은 아니다.&lt;br /&gt;&lt;code&gt;.gitignore&lt;/code&gt;는 Git에 올리지 않기 위한 설정이지, &lt;b&gt;AI 도구의 접근을 막는 보안 설정은 아니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Codex를 사용할 때는 실행 위치, 접근 가능한 폴더, 민감 파일 관리 방식을 따로 신경 써야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 하나는 변경 결과 검토다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex가 코드를 수정했다고 해서 바로 배포하면 안 된다.&lt;br /&gt;반드시 변경된 diff를 확인하고, 테스트를 실행하고, 예상하지 못한 수정이 들어갔는지 봐야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 아래 작업은 더 신중하게 다루는 편이 좋다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인증/인가 로직 변경&lt;/li&gt;
&lt;li&gt;결제 관련 코드 변경&lt;/li&gt;
&lt;li&gt;데이터 삭제 또는 마이그레이션&lt;/li&gt;
&lt;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;Codex는 개발 속도를 높여줄 수 있다.&lt;br /&gt;하지만 최종 판단은 여전히 개발자의 몫이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;어떤 상황에서 Codex를 쓰면 좋을까&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex는 단순 질문보다 실제 작업에 더 잘 맞는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래처럼 범위가 비교적 명확한 요청에 특히 유용하다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;이 API 응답 타입에 맞춰 프론트엔드 타입 정의를 수정해줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;기존 Button 컴포넌트 스타일을 유지하면서 loading 상태를 추가해줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;사용하지 않는 함수를 찾아 정리하고, 테스트가 깨지지 않는지 확인해줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;이 PR에서 위험해 보이는 변경이 있는지 리뷰해줘.&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;반대로 이런 질문은 ChatGPT가 더 편하다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;OAuth가 뭔지 설명해줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Next.js App Router와 Pages Router 차이를 알려줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;이 에러 메시지가 의미하는 게 뭐야?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 역할이 조금 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChatGPT는 이해하고 판단할 때 좋다.&lt;br /&gt;Codex는 실제 프로젝트를 고치고 반복 작업을 줄일 때 좋다.&lt;/p&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;먼저 ChatGPT로 방향을 잡고, Codex로 실제 코드 작업을 맡기는 식이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;개발자가 보는 Codex의 핵심 의미&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex를 단순한 코드 생성기로 보면 조금 아쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 &lt;b&gt;개발 환경에 들어온 AI 에이전트&lt;/b&gt;라는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전의 AI 코딩 도구는 자동완성에 가까웠다.&lt;br /&gt;개발자가 코드를 치면 다음 줄을 예측하거나, 함수 일부를 채워주는 방식이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex는 그보다 한 단계 더 나아간다.&lt;/p&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;물론 이 말이 개발자를 대체한다는 뜻은 아니다.&lt;/p&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;Codex를 잘 쓰려면 AI에게 모든 걸 맡기기보다 작업을 잘게 나누는 편이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 이런 식이다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;이 파일의 역할을 먼저 설명해줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;이 함수에 테스트를 추가해줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;이 컴포넌트의 중복 코드를 줄여줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&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;이렇게 요청하면 결과를 검토하기 쉽고, AI가 엉뚱한 방향으로 크게 수정할 가능성도 줄어든다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Codex와 ChatGPT, 무엇을 써야 할까&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기준은 어렵지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개념을 알고 싶으면 ChatGPT가 편하다.&lt;br /&gt;실제 프로젝트를 수정하고 싶으면 Codex가 더 잘 맞는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &amp;ldquo;JWT 인증이 뭔지 알려줘&amp;rdquo;는 ChatGPT에 묻는 게 좋다.&lt;br /&gt;반면 &amp;ldquo;이 프로젝트의 JWT 만료 처리 버그를 찾아서 수정해줘&amp;rdquo;는 Codex에 맡기는 쪽이 자연스럽다.&lt;/p&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;Codex는 개발자를 대신하는 도구라기보다, 프로젝트 안에서 함께 움직이는 보조 개발자에 가깝다.&lt;br /&gt;ChatGPT가 생각을 정리하는 도구라면, Codex는 그 생각을 실제 코드 작업으로 옮기는 도구에 가깝다.&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;</description>
      <category>AI/AI 코딩</category>
      <category>AI코딩</category>
      <category>chatGPT</category>
      <category>codex</category>
      <category>CodexCLI</category>
      <category>openAI</category>
      <category>개발도구</category>
      <category>개발자동화</category>
      <category>코드리뷰</category>
      <category>코딩에이전트</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/42</guid>
      <comments>https://notebase.tistory.com/entry/what-is-codex-chatgpt-difference#entry42comment</comments>
      <pubDate>Sat, 23 May 2026 13:34:27 +0900</pubDate>
    </item>
    <item>
      <title>Claude Code란? 터미널에서 사용하는 AI 코딩 도구 개념 정리</title>
      <link>https://notebase.tistory.com/entry/what-is-claude-code-terminal-ai-coding-tool</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code가 무엇인지, 일반 AI 챗봇&amp;middot;코드 자동완성 도구와 무엇이 다른지, 터미널에서 파일 수정과 명령 실행까지 돕는 AI 코딩 에이전트의 개념을 정리합니다.&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;Claude Code는 Anthropic이 제공하는 AI 코딩 도구입니다. 단순히 코드를 추천해주는 챗봇이 아니라, 개발자가 작업 중인 코드베이스를 읽고, 파일을 수정하고, 터미널 명령어 실행 흐름까지 도와주는 &lt;b&gt;AI 코딩 에이전트&lt;/b&gt;에 가깝습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 AI 챗봇은 보통 이런 방식으로 사용했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 복사합니다.&lt;br /&gt;질문을 입력합니다.&lt;br /&gt;답변을 다시 복사해서 IDE나 터미널에 붙여넣습니다.&lt;br /&gt;에러가 나면 다시 설명합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code는 이 중간 과정을 줄이는 데 초점이 있습니다. 개발자가 터미널에서 &amp;ldquo;이 버그 원인 찾아줘&amp;rdquo;, &amp;ldquo;테스트 실패 이유를 분석해줘&amp;rdquo;, &amp;ldquo;이 기능을 추가하기 전에 수정 범위를 정리해줘&amp;rdquo;처럼 요청하면, Claude가 프로젝트 파일을 읽고 관련 맥락을 바탕으로 작업을 도와줄 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2026년 5월 기준 Anthropic 공식 문서에서는 Claude Code를 코드베이스를 읽고, 파일을 편집하고, 명령을 실행하며, 개발 도구와 통합되는 agentic coding tool로 설명합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Claude Code는 왜 터미널 AI 코딩 도구라고 불릴까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code의 핵심 특징은 개발자가 이미 사용하는 터미널 안에서 동작한다는 점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 개발 작업은 IDE 안에서만 끝나지 않습니다. Git 명령어를 실행하고, 패키지를 설치하고, 테스트를 돌리고, 빌드 로그를 확인하고, 에러 메시지를 추적합니다. 이런 작업은 대부분 터미널과 연결되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code는 바로 이 흐름 안에 들어옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 폴더에서 Claude Code를 실행한 뒤 다음처럼 요청할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;이 프로젝트의 전체 구조를 설명해줘.
주요 디렉터리와 실행 흐름을 초보자 기준으로 정리해줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 이렇게 요청할 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&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;여기서 중요한 점은 Claude Code가 단순히 &amp;ldquo;터미널에서 대화하는 AI&amp;rdquo;가 아니라는 것입니다. 프로젝트 파일을 읽고, 필요한 경우 파일 수정안을 만들고, 테스트나 빌드 같은 명령 실행 흐름까지 함께 다룰 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, Claude Code는 터미널용 챗봇이라기보다 &lt;b&gt;읽기&amp;middot;수정&amp;middot;실행 도구를 가진 코딩 에이전트&lt;/b&gt;에 가깝습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Claude Code와 일반 AI 챗봇의 차이&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code를 이해하려면 먼저 Claude 웹 채팅이나 ChatGPT 같은 일반 AI 챗봇과 비교해보는 것이 좋습니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;일반 AI 챗봇&lt;/th&gt;
&lt;th&gt;Claude Code&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;사용 위치&lt;/td&gt;
&lt;td&gt;웹, 앱&lt;/td&gt;
&lt;td&gt;터미널, IDE 등 개발 환경&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;코드 이해 방식&lt;/td&gt;
&lt;td&gt;사용자가 붙여넣은 코드 중심&lt;/td&gt;
&lt;td&gt;프로젝트 파일을 직접 읽으며 파악&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;작업 범위&lt;/td&gt;
&lt;td&gt;설명, 코드 예시, 문제 해결 조언&lt;/td&gt;
&lt;td&gt;파일 수정, 명령 실행, 테스트 보조, Git 작업 보조&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;개발 흐름&lt;/td&gt;
&lt;td&gt;복사&amp;middot;붙여넣기 필요&lt;/td&gt;
&lt;td&gt;터미널 작업 흐름 안에서 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;성격&lt;/td&gt;
&lt;td&gt;코딩 상담 도구&lt;/td&gt;
&lt;td&gt;코딩 에이전트 도구&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 AI 챗봇은 설명에 강합니다. 코드 예시를 만들거나 에러 메시지를 해석하는 데도 유용합니다. 하지만 실제 프로젝트에 적용하려면 개발자가 코드를 복사하고, 붙여넣고, 수정하고, 다시 테스트해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code는 이 흐름을 줄이는 방향입니다. 프로젝트 구조를 확인하고, 여러 파일을 함께 읽고, 필요한 수정 범위를 제안할 수 있습니다. 경우에 따라 파일 변경과 명령 실행까지 개발 흐름 안에서 이어갈 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 Claude Code도 최종 판단을 대신해주는 도구는 아닙니다. 제안된 변경 사항을 검토하고, 실제 반영 여부를 결정하는 책임은 여전히 개발자에게 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Claude Code와 코드 자동완성 도구의 차이&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GitHub Copilot 같은 코드 자동완성 도구는 개발자가 작성 중인 코드의 다음 줄이나 다음 블록을 추천하는 방식에 가깝습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 자동완성 도구는 이런 상황에 강합니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&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;반면 Claude Code는 작업 단위가 더 큽니다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;회원가입 API에 이메일 인증 단계를 추가하려고 해.
현재 구조를 먼저 파악하고, 수정해야 할 파일과 테스트 계획을 설명한 뒤 진행해줘.&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;ldquo;지금 쓰는 코드의 다음 부분&amp;rdquo;을 돕는다면, Claude Code는 &amp;ldquo;개발 작업의 흐름&amp;rdquo;을 돕는 쪽에 가깝습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Claude Code는 다음과 같은 작업에 더 잘 맞습니다.&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;li&gt;리팩터링 계획 수립&lt;/li&gt;
&lt;li&gt;Git 변경 사항 요약&lt;/li&gt;
&lt;li&gt;문서 업데이트&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Claude Code의 핵심은 실행 권한에 있다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code를 기존 CLI 챗봇과 구분하는 중요한 지점은 &lt;b&gt;도구 사용 권한&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널에서 AI와 대화만 하는 도구라면, 결국 답변을 사람이 복사해서 실행해야 합니다. 하지만 Claude Code는 개발 작업에 필요한 도구를 사용할 수 있도록 설계되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 흐름이 가능합니다.&lt;/p&gt;
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;테스트가 실패하는 원인을 찾아줘.
관련 파일을 확인하고, 필요한 경우 수정한 뒤 `npm test`로 다시 확인해줘.&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;이 요청에서 Claude Code는 단순히 테스트 실패 원인을 추측하는 데 그치지 않습니다. 프로젝트 파일을 읽고, 실패 원인을 추적하고, 수정안을 만들고, 테스트 실행 흐름까지 이어갈 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 모든 작업을 자유롭게 실행한다는 뜻은 아닙니다. Claude Code는 권한 기반 구조를 사용합니다. 공식 보안 문서에 따르면 기본적으로 읽기 중심 권한을 사용하며, 파일 편집이나 테스트 실행, 명령 실행처럼 추가 작업이 필요할 때는 사용자의 명시적 승인을 요청합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조는 장점과 주의점이 함께 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장점은 개발자가 반복 작업을 줄일 수 있다는 점입니다. 반대로 주의할 점은 AI가 실행하려는 명령과 변경하려는 파일을 개발자가 직접 확인해야 한다는 점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 다음과 같은 작업은 더 신중하게 검토해야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인증&amp;middot;인가 로직 수정&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;/li&gt;
&lt;li&gt;개인정보 처리 코드 수정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code로 파일을 수정했다면 바로 커밋하지 말고 &lt;code&gt;git diff&lt;/code&gt;로 변경 내용을 확인하는 습관이 필요합니다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;git diff&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;AI가 만든 코드는 초안으로 보고, 최종 리뷰는 사람이 한다는 기준을 세워두는 편이 안전합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Claude Code로 할 수 있는 일&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code는 프로젝트 안에서 여러 개발 작업을 보조할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 코드베이스 구조 파악&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새 프로젝트를 열었을 때 가장 먼저 막히는 부분은 구조 파악입니다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&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;Claude Code는 프로젝트 파일을 읽고 어떤 폴더가 어떤 역할을 하는지 설명할 수 있습니다. 낯선 코드베이스에 합류했을 때 유용합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 버그 원인 분석&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러 메시지만 보고 원인을 찾기 어려울 때도 활용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;이 테스트가 실패하는 원인을 찾아줘.
실패 로그를 보고 관련 파일을 확인한 뒤 수정 방향을 제안해줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 에러 메시지를 해석하는 수준을 넘어, 관련 파일을 함께 확인하면서 원인을 추적할 수 있다는 점이 장점입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 리팩터링 계획 수립&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 파일에 걸친 리팩터링도 Claude Code의 활용 범위에 들어갑니다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;중복된 API 응답 처리 로직을 공통 함수로 분리하고 싶어.
기존 동작을 바꾸지 않는 선에서 수정 계획을 먼저 작성해줘.&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;리팩터링은 한 파일만 고치는 작업이 아닌 경우가 많습니다. Claude Code는 코드베이스의 여러 파일을 함께 읽을 수 있기 때문에 수정 범위를 정리하는 데 도움이 됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. 테스트 작성과 실행 보조&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트가 없는 코드에 테스트를 추가하거나, 실패하는 테스트를 고치는 데도 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;userService의 예외 케이스 테스트를 추가해줘.
수정 후 npm test를 실행하고 실패하면 원인을 설명해줘.&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;테스트 명령어를 명확히 알려주면 Claude Code가 결과를 검증하기 쉬워집니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5. Git 작업 보조&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code는 Git 작업 흐름에서도 활용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;현재 변경된 파일을 요약해줘.
커밋 메시지 후보를 3개 작성해줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 다음처럼 요청할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;마지막 5개 커밋을 확인하고, 이번 변경 사항과 충돌 가능성이 있는 부분을 찾아줘.&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Claude Code 설치 방법&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2026년 5월 기준 공식 문서에서는 Claude Code를 사용하기 위해 터미널, 작업할 코드 프로젝트, Claude 구독 계정 또는 Claude Console 계정 등이 필요하다고 안내합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치 명령어는 운영체제별로 구분해서 보는 것이 좋습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;macOS / Linux / WSL&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;curl -fsSL https://claude.ai/install.sh | bash&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Windows PowerShell&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;irm https://claude.ai/install.ps1 | iex&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Windows CMD&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;dos&quot;&gt;&lt;code&gt;curl -fsSL https://claude.ai/install.cmd -o install.cmd &amp;amp;&amp;amp; install.cmd &amp;amp;&amp;amp; del install.cmd&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;설치 후에는 프로젝트 폴더로 이동해 Claude Code를 실행합니다.&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;cd /path/to/your/project
claude&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;처음 실행하면 로그인 절차가 필요합니다. 공식 문서에 따르면 Claude Pro, Max, Team, Enterprise 계정이나 Claude Console 계정 등을 사용할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Claude Code를 잘 쓰는 요청 방식&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code는 강력하지만, &amp;ldquo;알아서 다 해줘&amp;rdquo;라고만 요청하면 기대한 결과가 나오지 않을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 요청은 보통 다음 조건을 포함합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 먼저 탐색하게 하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 코드를 수정하게 하기보다, 먼저 구조를 파악하게 하는 것이 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;src/auth 폴더를 읽고 현재 로그인 흐름을 설명해줘.
아직 파일은 수정하지 말고, 수정이 필요한 부분만 정리해줘.&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;이렇게 요청하면 Claude Code가 무리하게 코드를 바꾸기 전에 현재 구조를 설명합니다. 개발자는 그 설명을 보고 방향이 맞는지 먼저 판단할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 성공 기준을 명확히 주기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;나쁜 예시&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;로그인 버그 고쳐줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 요청은 증상, 관련 영역, 기대 결과가 분명하지 않아 작업 범위가 넓어질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;더 나은 예시&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;세션 만료 후 다시 로그인할 때 401 에러가 발생해.
src/auth와 token refresh 로직을 확인해줘.
재현 테스트를 먼저 만들고, 테스트가 통과하도록 수정해줘.&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;증상, 관련 영역, 기대 결과를 함께 적으면 Claude Code가 작업 범위를 더 정확히 잡을 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 테스트 명령어를 알려주기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트마다 테스트 실행 방식은 다릅니다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;수정 후 npm test를 실행해줘.
타입 체크는 npm run typecheck로 확인해줘.&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;또는 Python 프로젝트라면 이렇게 쓸 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;수정 후 pytest를 실행해줘.
실패하는 테스트가 있으면 원인을 설명하고 수정 방향을 제안해줘.&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;테스트 명령어를 알려주면 Claude Code가 작업 결과를 검증하기 쉬워집니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. 큰 작업은 단계로 나누기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큰 기능을 한 번에 맡기면 결과를 검토하기 어렵습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1단계로 현재 구조만 파악해줘.
2단계에서 수정 계획을 작성해줘.
3단계에서 내가 승인하면 코드 수정을 진행해줘.&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;CLAUDE.md 파일은 언제 쓸까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code에는 프로젝트용 지침을 담는 &lt;code&gt;CLAUDE.md&lt;/code&gt; 파일을 둘 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일에는 코드 스타일, 테스트 명령어, 브랜치 규칙, PR 작성 방식 같은 내용을 적어둘 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;# Code style
- TypeScript에서는 명시적인 타입을 선호한다.
- 공통 유틸 함수는 src/utils에 둔다.

# Workflow
- 코드 수정 후 `npm run typecheck`를 실행한다.
- 테스트는 가능하면 관련 테스트만 먼저 실행한다.
- 파일 수정 후 `git diff`로 변경 내용을 요약한다.&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;code&gt;CLAUDE.md&lt;/code&gt;를 너무 길게 작성하는 것은 좋지 않습니다. 항상 필요한 규칙만 짧게 적는 편이 낫습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트마다 반복되는 작업 규칙이 있다면 &lt;code&gt;CLAUDE.md&lt;/code&gt;에 넣고, 특정 상황에서만 필요한 긴 절차는 별도 문서나 스킬로 분리하는 방식이 더 관리하기 쉽습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;비용과 토큰 사용량도 주의해야 한다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code를 사용할 때는 비용과 사용량도 고려해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 비용 문서에 따르면 Claude Code 비용은 API 토큰 소비, 모델 선택, 코드베이스 크기, 사용 패턴 등에 따라 달라질 수 있습니다. Pro나 Max 같은 구독 플랜을 사용하는 경우와 API 기반으로 사용하는 경우의 비용 체감도 다를 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 큰 코드베이스에서 여러 파일을 읽고, 테스트를 반복 실행하고, 긴 대화를 이어가면 사용량이 빠르게 늘 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비용을 줄이려면 다음 습관이 도움이 됩니다.&lt;/p&gt;
&lt;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;불필요하게 &amp;ldquo;전체 프로젝트를 다 읽어줘&amp;rdquo;라고 요청하지 않기&lt;/li&gt;
&lt;li&gt;긴 작업은 단계별로 나누기&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/usage&lt;/code&gt; 같은 사용량 확인 기능 활용하기&lt;/li&gt;
&lt;li&gt;팀 단위라면 사용량과 비용 추적 기준을 정해두기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음 요청은 범위가 너무 넓습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;범위가 넓은 예시&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;이 프로젝트 전체를 분석해서 다 고쳐줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음처럼 좁히는 편이 낫습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;범위를 좁힌 예시&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;src/auth 폴더 안에서 로그인 실패와 관련된 파일만 확인해줘.
아직 수정하지 말고 원인 후보를 먼저 정리해줘.&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;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;code&gt;.gitignore&lt;/code&gt;는 Claude Code 접근 제어 장치가 아니다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code를 사용할 때 민감한 파일이나 불필요한 경로를 어떻게 다룰지도 생각해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 주의할 점이 있습니다. &lt;code&gt;.gitignore&lt;/code&gt;는 Git이 추적하지 않을 파일을 지정하는 설정입니다. Claude Code의 파일 접근 권한을 제어하기 위한 장치로 이해하면 안 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;민감한 파일이나 특정 경로 접근을 제한하려면 Claude Code의 &lt;code&gt;settings&lt;/code&gt;, &lt;code&gt;permissions&lt;/code&gt;, &lt;code&gt;sandbox&lt;/code&gt; 관련 설정을 확인하는 편이 안전합니다. 공식 설정 문서에서는 파일 시스템과 네트워크 제한을 &lt;code&gt;sandbox.filesystem&lt;/code&gt; 설정과 &lt;code&gt;permission rules&lt;/code&gt;로 구성할 수 있다고 설명합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무에서는 다음 기준을 두는 것이 좋습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;.env&lt;/code&gt;, 인증 키, 비밀 토큰 파일은 코드베이스에 두지 않기&lt;/li&gt;
&lt;li&gt;민감한 파일은 Claude가 읽거나 수정할 필요가 없도록 작업 범위에서 제외하기&lt;/li&gt;
&lt;li&gt;권한 설정으로 읽기&amp;middot;쓰기&amp;middot;웹 접근 범위 관리하기&lt;/li&gt;
&lt;li&gt;신뢰하기 어려운 저장소에서는 자동 승인 모드 사용을 피하기&lt;/li&gt;
&lt;li&gt;AI가 실행하려는 명령은 승인 전에 확인하기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code는 개발자의 생산성을 높이는 도구지만, 권한을 가진 도구이기 때문에 보안 기준도 함께 세워야 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Claude Code의 장점&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code의 장점은 크게 세 가지입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫째, 기존 개발 흐름을 크게 바꾸지 않아도 됩니다.&lt;br /&gt;터미널에서 바로 사용할 수 있고, Git이나 테스트 명령어 같은 기존 도구와 함께 사용할 수 있습니다.&lt;/p&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;셋째, 반복적인 개발 작업을 줄일 수 있습니다.&lt;br /&gt;테스트 작성, 에러 로그 분석, 리팩터링 후보 정리, 문서화 같은 작업에서 특히 유용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 이 장점은 개발자가 결과를 검토할 수 있을 때 더 의미가 있습니다. Claude Code는 개발자를 대체하는 도구라기보다, 개발자가 더 빠르게 탐색하고 수정할 수 있도록 돕는 도구에 가깝습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Claude Code의 한계와 주의할 점&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code가 프로젝트 파일을 읽고 수정할 수 있다고 해서 결과를 그대로 믿으면 안 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 도구는 여전히 잘못된 판단을 할 수 있습니다. 프로젝트의 의도나 비즈니스 규칙을 완전히 이해하지 못한 상태에서 그럴듯한 코드를 만들 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 다음 작업은 반드시 사람이 검토해야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인증&amp;middot;보안 로직 수정&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;/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;또한 컨텍스트가 길어질수록 품질이 떨어질 수 있습니다. 대화가 길어지고 읽어야 할 파일이 많아질수록 AI가 이전 지시를 놓치거나, 작업 범위를 넓게 해석할 가능성이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큰 작업은 한 번에 맡기기보다 다음처럼 나누는 편이 안전합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1단계: 현재 구조 파악
2단계: 수정 계획 작성
3단계: 핵심 파일만 수정
4단계: 테스트 실행
5단계: git diff 요약
6단계: 사람이 최종 리뷰&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;이 흐름을 지키면 Claude Code의 생산성은 활용하면서도 위험한 변경을 줄일 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;어떤 사람에게 Claude Code가 잘 맞을까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code는 다음과 같은 개발자에게 잘 맞습니다.&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;AI에게 단순 코드 생성보다 작업 흐름 보조를 맡기고 싶은 사람&lt;/li&gt;
&lt;li&gt;Git, 빌드, 테스트 명령어를 자주 사용하는 개발자&lt;/li&gt;
&lt;li&gt;여러 파일에 걸친 변경 사항을 AI와 함께 검토하고 싶은 사람&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 터미널 사용이 낯설거나, 코드 수정 결과를 직접 검토하기 어려운 초보자라면 처음부터 큰 작업을 맡기기보다 설명&amp;middot;분석 용도로 시작하는 편이 안전합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 첫 사용은 이런 요청이 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;이 프로젝트가 어떤 구조인지 설명해줘.
아직 파일은 수정하지 말고, 주요 폴더와 실행 흐름만 정리해줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 다음처럼 쓸 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&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;이렇게 읽기 중심 작업부터 시작하면 Claude Code의 동작 방식을 이해하기 쉽습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Claude Code는 챗봇보다 개발자 도구에 가깝다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Code를 단순히 &amp;ldquo;코딩해주는 AI&amp;rdquo;로 보면 장점을 제대로 이해하기 어렵습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 터미널 안에서 프로젝트를 읽고, 계획을 세우고, 파일을 수정하고, 테스트나 Git 같은 개발 명령 흐름까지 다룰 수 있다는 점입니다. 그래서 Claude Code는 질문에 답하는 챗봇이라기보다 개발자의 작업 공간 안에 들어온 AI 코딩 에이전트에 가깝습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 모든 코드를 믿고 맡길 수 있는 도구는 아닙니다. 최종 책임은 여전히 개발자에게 있습니다. 하지만 구조 파악, 버그 분석, 테스트 작성, 리팩터링, 문서화처럼 시간이 많이 드는 작업에서는 충분히 생산성을 높일 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 사용한다면 큰 기능 개발부터 맡기기보다, 현재 프로젝트를 설명하게 하거나 실패한 테스트 원인을 찾게 하는 작은 작업부터 시작하는 것이 좋습니다. 그 과정에서 요청 범위를 좁히고, 권한을 확인하고, &lt;code&gt;git diff&lt;/code&gt;와 테스트로 결과를 검증하는 습관을 들이면 더 안정적으로 활용할 수 있습니다.&lt;/p&gt;</description>
      <category>AI/AI 코딩</category>
      <category>AI 에이전트</category>
      <category>ai 코딩 도구</category>
      <category>Anthropic</category>
      <category>claude code</category>
      <category>개발 도구</category>
      <category>코딩 자동화</category>
      <category>터미널</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/41</guid>
      <comments>https://notebase.tistory.com/entry/what-is-claude-code-terminal-ai-coding-tool#entry41comment</comments>
      <pubDate>Sat, 23 May 2026 13:03:19 +0900</pubDate>
    </item>
    <item>
      <title>Context7이란? 최신 개발 문서를 AI가 참고하게 만드는 방법</title>
      <link>https://notebase.tistory.com/entry/what-is-context7-mcp-ai-coding-docs</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Context7은 AI 코딩 도구가 최신 개발 문서와 코드 예제를 참고하도록 돕는 MCP 기반 도구입니다. Context7의 개념, MCP와의 관계, 사용 방식, 주의할 점을 초보자도 이해하기 쉽게 정리했습니다.&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;AI 코딩 도구를 쓰다 보면 Context7이 자주 언급됩니다. Context7은 AI가 오래된 기억만으로 코드를 작성하지 않도록, 최신 개발 문서와 코드 예제를 프롬프트에 넣어주는 MCP 기반 도구입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자는 AI에게 질문합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;Next.js 미들웨어 코드 작성해줘.&amp;rdquo;&lt;br /&gt;&amp;ldquo;Supabase 로그인 구현해줘.&amp;rdquo;&lt;br /&gt;&amp;ldquo;Cloudflare Workers 캐싱 예제 만들어줘.&amp;rdquo;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 여기서 시작됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 답은 해주지만, 그 답이 현재 버전과 맞지 않을 수 있습니다. 예전 문법을 쓰거나, 더 이상 존재하지 않는 API를 알려주거나, 특정 라이브러리의 최신 변경사항을 반영하지 못하는 경우가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context7은 이 문제를 줄이기 위해 등장한 도구입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2026년 5월 기준 Context7 공식 문서는 Context7을 최신 버전별 문서와 코드 예제를 AI 코딩 어시스턴트에 직접 가져오는 도구로 설명합니다. 공식 문서에서는 AI가 오래된 학습 데이터나 일반적인 답변에 의존하는 문제를 줄이는 것이 핵심 목적이라고 안내합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;AI 코딩 도구가 틀린 코드를 알려주는 이유&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 도구는 매우 편리합니다.&lt;br /&gt;하지만 개발 문서가 빠르게 바뀌는 분야에서는 한계가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 상황이 생길 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최신 Next.js 버전과 맞지 않는 예제를 알려준다.&lt;/li&gt;
&lt;li&gt;Supabase 인증 API를 예전 방식으로 설명한다.&lt;/li&gt;
&lt;li&gt;React의 최신 권장 패턴을 반영하지 못한다.&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;이런 문제는 AI가 무능해서라기보다, AI가 답변할 때 참고하는 정보가 항상 최신 공식 문서와 연결되어 있는 것은 아니기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 프레임워크, 클라우드 서비스, SDK, 인증 라이브러리는 업데이트가 잦습니다.&lt;br /&gt;1년 전에는 맞던 코드가 지금은 경고를 띄우거나, 아예 작동하지 않을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 AI 코딩에서는 단순히 &amp;ldquo;똑똑한 모델&amp;rdquo;만 중요한 것이 아닙니다.&lt;br /&gt;AI가 어떤 문서를 참고하고 있는지도 중요합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Context7이란?&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2060&quot; data-origin-height=&quot;2080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJ5qKX/dJMcabqNfKP/tUyF6sH8F5HZErONhpDk71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJ5qKX/dJMcabqNfKP/tUyF6sH8F5HZErONhpDk71/img.png&quot; data-alt=&quot;Context7 홈페이지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJ5qKX/dJMcabqNfKP/tUyF6sH8F5HZErONhpDk71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJ5qKX%2FdJMcabqNfKP%2FtUyF6sH8F5HZErONhpDk71%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;572&quot; height=&quot;578&quot; data-origin-width=&quot;2060&quot; data-origin-height=&quot;2080&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Context7 홈페이지&lt;/figcaption&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;Context7은 AI 코딩 도구가 최신 개발 문서를 참고할 수 있도록 돕는 문서 컨텍스트 도구입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 쉽게 말하면, Context7은 AI에게 이렇게 말해주는 역할을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;그냥 네가 기억하는 대로 답하지 말고, 지금 이 라이브러리의 최신 문서를 보고 답해.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context7 공식 문서에 따르면, Context7은 최신 문서와 버전별 코드 예제를 가져와 AI의 프롬프트 안에 직접 넣어줍니다. 사용자는 Cursor 같은 AI 코딩 도구에서 프롬프트 끝에 &lt;code&gt;use context7&lt;/code&gt;을 붙여 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 이런 식입니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;Create a Next.js middleware that checks for a valid JWT in cookies.
use context7&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;code&gt;use context7&lt;/code&gt;입니다. 이 문구는 AI에게 Context7을 통해 관련 문서를 참고하라는 신호로 쓰입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 다음처럼 요청할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Configure a Cloudflare Worker script to cache JSON API responses for five minutes.
use context7&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;AI에게 질문만 던지는 것이 아니라, 해당 작업과 관련된 최신 문서까지 함께 참고하게 만드는 것입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;MCP와 Context7의 관계&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context7을 이해하려면 MCP도 함께 알아야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP는 Model Context Protocol의 약자입니다.&lt;br /&gt;AI 모델이나 AI 에이전트가 외부 도구, 데이터, 문서, 서비스와 연결될 수 있도록 만든 표준 방식에 가깝습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 AI는 사용자가 입력한 프롬프트와 모델이 이미 알고 있는 지식을 바탕으로 답합니다.&lt;br /&gt;반면 MCP를 사용하면 AI가 외부 도구를 통해 추가 정보를 가져올 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context7은 이 MCP 생태계에서 &amp;ldquo;최신 개발 문서 제공&amp;rdquo;에 초점을 둔 도구입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 관계를 단순하게 정리하면 이렇습니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;역할&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MCP&lt;/td&gt;
&lt;td&gt;AI와 외부 도구를 연결하는 방식&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Context7&lt;/td&gt;
&lt;td&gt;MCP를 통해 최신 개발 문서를 AI에게 전달하는 도구&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI 코딩 도구&lt;/td&gt;
&lt;td&gt;Context7이 가져온 문서를 참고해 코드 작성&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;MCP 디렉터리에서도 Context7은 공식 MCP 서버로 소개되며, 공식 출처의 최신 버전별 코드 문서와 예제를 AI 프롬프트에 주입하는 도구로 설명됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Context7은 어떻게 동작할까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context7의 작동 방식은 복잡해 보이지만, 흐름은 비교적 단순합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 사용자가 AI에게 개발 작업을 요청한다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Cursor에서 다음처럼 입력합니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Next.js 15 기준으로 middleware에서 인증 체크하는 예제 작성해줘. use context7&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. AI 코딩 도구가 Context7 MCP 서버를 호출한다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;use context7&lt;/code&gt;이라는 지시가 있으면, AI 코딩 도구는 Context7을 통해 관련 문서를 찾습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 사용 시에는 Cursor, Claude Code, Windsurf, Cline 같은 AI 코딩 도구의 MCP 설정 메뉴에서 Context7 MCP 서버를 등록한 뒤 사용합니다. 도구마다 설정 방식은 다르지만, 핵심은 AI 코딩 도구가 Context7 서버를 외부 문서 조회 도구로 호출할 수 있게 연결하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치 명령어나 설정 파일 형식은 사용하는 도구에 따라 달라질 수 있습니다. 따라서 개념을 먼저 이해한 뒤, 실제 적용 단계에서는 각 도구의 MCP 설정 문서를 함께 확인하는 것이 좋습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. Context7이 관련 라이브러리 문서를 가져온다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context7은 요청과 관련된 라이브러리나 프레임워크 문서를 찾아 AI의 컨텍스트에 넣습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 설명에 따르면 Context7은 최신 코드 예제와 문서를 LLM의 컨텍스트 안으로 가져와, 탭을 옮겨가며 문서를 찾거나 오래된 API를 기반으로 답변받는 문제를 줄이는 것을 목표로 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. AI가 문서를 참고해 답변한다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI는 이제 단순히 기억에만 의존하지 않고, Context7이 제공한 문서 내용을 바탕으로 답변합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 이것이 모든 오류를 완전히 없앤다는 뜻은 아닙니다.&lt;br /&gt;하지만 적어도 변화가 빠른 라이브러리에서는 오래된 코드가 나올 가능성을 줄이는 데 도움이 됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;code&gt;use context7&lt;/code&gt;은 무슨 뜻일까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context7을 검색하다 보면 &lt;code&gt;use context7&lt;/code&gt;이라는 표현을 자주 보게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 말은 AI에게 다음과 같이 지시하는 것과 비슷합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;이 작업은 Context7을 사용해서 최신 문서를 참고한 뒤 답해줘.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context7 공식 문서에서도 자연어 프롬프트 끝에 &lt;code&gt;use context7&lt;/code&gt;을 붙이는 방식을 예시로 안내하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 그냥 이렇게 묻는 것과,&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;Supabase 이메일 회원가입 코드 작성해줘&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 묻는 것은 차이가 있을 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;Supabase 이메일 회원가입 코드 작성해줘. use context7&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;두 번째 요청은 AI가 Context7을 통해 Supabase 관련 최신 문서나 예제를 참고하도록 유도합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 라이브러리 버전이 중요할 때는 더 구체적으로 쓰는 것이 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Supabase JavaScript client 최신 문서 기준으로 이메일 회원가입 예제를 작성해줘. use context7&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 다음처럼 버전을 명시할 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Next.js 15 App Router 기준으로 middleware 인증 처리 예제를 작성해줘. use context7&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;좋은 프롬프트는 Context7의 효과를 더 높입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Context7이 유용한 상황&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context7은 모든 질문에 반드시 필요한 도구는 아닙니다.&lt;br /&gt;하지만 다음 상황에서는 꽤 유용할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 변화가 빠른 프레임워크를 사용할 때&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js, React, SvelteKit, Nuxt 같은 프레임워크는 버전에 따라 권장 방식이 자주 바뀝니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 예전 방식으로 코드를 작성하면, 실행은 되더라도 현재 공식 문서와 맞지 않을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴 때 Context7을 사용하면 최신 문서 기반의 답변을 받을 가능성이 높아집니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 클라우드 서비스 SDK를 사용할 때&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Supabase, Firebase, Cloudflare, Stripe, Vercel 같은 서비스는 SDK와 설정 방식이 바뀌는 경우가 많습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 인증, 결제, 배포, 캐싱, 웹훅 같은 기능은 예전 코드가 그대로 통하지 않을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context7은 이런 서비스형 개발 문서를 AI가 참고하게 만들 때 유용합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. AI가 없는 API를 만들어낼 때&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 도구를 쓰다 보면 실제로는 존재하지 않는 함수나 옵션을 제안하는 경우가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이른바 &amp;ldquo;환각&amp;rdquo; 문제입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context7은 공식 문서 기반의 예제를 컨텍스트로 넣어주기 때문에, 이런 문제를 줄이는 데 도움이 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 문서 역시 Context7의 목적 중 하나로 존재하지 않는 API를 만들어내는 문제를 줄이는 것을 언급합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. 문서를 직접 찾는 시간이 아까울 때&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자는 종종 코드 작성보다 문서 탐색에 더 많은 시간을 씁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 문서 탭을 열고, 버전을 확인하고, 예제를 찾고, 현재 프로젝트에 맞는지 비교해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context7은 이 과정을 AI 코딩 흐름 안으로 가져옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 문서를 아예 보지 않아도 된다는 뜻은 아닙니다.&lt;br /&gt;다만 AI에게 초안을 맡길 때 참고 자료를 더 정확하게 넣어줄 수 있다는 의미입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Context7을 쓴다고 항상 정답이 나올까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇지는 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context7은 AI 답변의 근거를 최신 문서에 더 가깝게 만들어주는 도구입니다.&lt;br /&gt;하지만 다음 한계는 여전히 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 프로젝트 상황까지 완전히 이해하는 것은 아니다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context7은 라이브러리 문서를 가져오는 데 강점이 있습니다.&lt;br /&gt;하지만 내 프로젝트의 폴더 구조, 기존 코드 스타일, 내부 아키텍처, 팀 규칙까지 자동으로 완벽하게 이해하는 것은 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 실무에서는 Context7과 함께 프로젝트 문맥도 같이 제공하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 다음처럼 요청할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;현재 프로젝트는 Next.js 15 App Router를 사용하고 있어.
인증은 Supabase Auth를 쓰고 있고, middleware.ts에서 보호 라우트를 처리하려고 해.
공식 문서 기준으로 구현 방향을 제안해줘. use context7&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 문서가 최신이어도 해석은 AI가 한다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context7이 문서를 가져와도, 최종 코드를 작성하는 것은 AI입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 문서를 잘못 해석할 수도 있고, 프로젝트 상황에 맞지 않는 코드를 만들 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 생성된 코드는 반드시 실행하고 검증해야 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 보안 관련 코드는 추가 검토가 필요하다&lt;/b&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;문서 기반이라고 해도 바로 운영 환경에 넣기보다는 다음을 확인해야 합니다.&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;Context7은 문서 참고를 도와주는 도구이지, 보안 검토를 대신하는 도구는 아닙니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;MCP, CLI, Skills와 비교하면 무엇이 다를까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 개발 도구를 쓰다 보면 Context7 외에도 MCP, CLI, Skills 같은 용어를 접하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 역할은 다릅니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;핵심 역할&lt;/th&gt;
&lt;th&gt;Context7과의 관계&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MCP&lt;/td&gt;
&lt;td&gt;AI와 외부 도구를 연결하는 표준 방식&lt;/td&gt;
&lt;td&gt;Context7이 MCP 서버 형태로 제공됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Context7&lt;/td&gt;
&lt;td&gt;최신 개발 문서를 AI에게 제공&lt;/td&gt;
&lt;td&gt;MCP를 활용한 문서 컨텍스트 도구&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CLI&lt;/td&gt;
&lt;td&gt;터미널에서 명령어로 실행하는 도구&lt;/td&gt;
&lt;td&gt;설치, 실행, 자동화에 유리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Skills&lt;/td&gt;
&lt;td&gt;특정 작업 방식을 AI에게 학습시키는 지침 묶음&lt;/td&gt;
&lt;td&gt;문서 검색보다는 작업 절차 표준화에 가까움&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 비유하면 이렇습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP는 AI가 외부 도구와 연결되는 통로입니다.&lt;br /&gt;Context7은 그 통로를 통해 최신 개발 문서를 가져오는 도구입니다.&lt;br /&gt;CLI는 개발자가 직접 명령어로 실행하는 방식입니다.&lt;br /&gt;Skills는 AI가 특정 작업을 더 일관되게 수행하도록 만드는 작업 규칙에 가깝습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 Context7은 &amp;ldquo;AI가 최신 문서를 참고하게 만드는 것&amp;rdquo;에 초점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 Skills는 &amp;ldquo;AI가 특정 작업 방식을 따르게 만드는 것&amp;rdquo;에 더 가깝습니다.&lt;/p&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;예를 들어 다음과 같이 역할을 나눌 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Context7: 최신 라이브러리 문서 참고&lt;/li&gt;
&lt;li&gt;프로젝트 규칙 파일 또는 Skills: 코드 스타일, 테스트 방식, 폴더 구조 안내&lt;/li&gt;
&lt;li&gt;CLI: 실제 빌드, 테스트, 린트 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Context7을 잘 쓰는 프롬프트 예시&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context7은 그냥 붙여도 되지만, 요청을 구체적으로 쓰면 더 좋습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;나쁜 예시&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;로그인 만들어줘. use context7&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;br /&gt;어떤 프레임워크인지, 어떤 인증 서비스를 쓰는지, 어떤 환경인지 알기 어렵습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;더 나은 예시&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Next.js 15 App Router와 Supabase Auth를 사용해서
이메일/비밀번호 로그인 페이지를 만들고 싶어.
공식 문서 기준으로 필요한 파일 구조와 예제 코드를 제안해줘. use context7&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;더 실무적인 예시&lt;/b&gt;&lt;/h3&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;현재 프로젝트는 Next.js 15 App Router, TypeScript, Supabase Auth를 사용해.
로그인하지 않은 사용자가 /dashboard에 접근하면 /login으로 보내고 싶어.
middleware.ts 구현 예제를 최신 문서 기준으로 작성해줘.
주의해야 할 보안 포인트도 함께 설명해줘. use context7&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;Context7은 문서를 가져오는 도구입니다.&lt;br /&gt;하지만 어떤 문서를 참고해야 하는지 힌트를 주는 것은 여전히 사용자의 몫입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Context7이 특히 잘 맞는 개발자&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context7은 다음 유형의 개발자에게 잘 맞습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AI 코딩 도구를 자주 쓰는 개발자&lt;/li&gt;
&lt;li&gt;최신 프레임워크를 빠르게 따라가야 하는 개발자&lt;/li&gt;
&lt;li&gt;공식 문서를 매번 직접 뒤지는 시간이 부담스러운 사람&lt;/li&gt;
&lt;li&gt;AI가 오래된 코드 예제를 주는 것에 불편함을 느낀 사람&lt;/li&gt;
&lt;li&gt;Cursor, Claude Code, Windsurf, Cline 같은 도구를 실무에 쓰는 사람&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 다음 상황에서는 필요성이 낮을 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;거의 변하지 않는 사내 레거시 코드만 다루는 경우&lt;/li&gt;
&lt;li&gt;인터넷 문서보다 내부 문서와 코드베이스 규칙이 더 중요한 경우&lt;/li&gt;
&lt;li&gt;AI 코딩 도구를 거의 쓰지 않는 경우&lt;/li&gt;
&lt;li&gt;단순한 문법 질문 위주로만 AI를 사용하는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Context7을 사용할 때의 현실적인 기준&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context7은 AI 코딩의 정확도를 높이는 데 도움이 될 수 있습니다.&lt;br /&gt;하지만 &amp;ldquo;AI가 알아서 최신 문서를 완벽히 보고, 완벽한 코드를 작성한다&amp;rdquo;로 이해하면 안 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현실적인 사용 기준은 이렇습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;최신 API가 중요한 작업에 적용합니다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js, Supabase, Stripe, Cloudflare, React 관련 작업은 문서 변경 영향이 크기 때문에 Context7을 붙여볼 만합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;버전과 환경을 함께 명시합니다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;최신 기준&amp;rdquo;보다 &amp;ldquo;Next.js 15 App Router 기준&amp;rdquo;처럼 쓰는 편이 좋습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;생성된 코드는 반드시 실행해봅니다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context7은 참고 문서를 보강해주는 도구이지, 테스트를 대신하지 않습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;보안과 결제 코드는 공식 문서를 다시 확인합니다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증, 권한, 결제, 토큰 검증처럼 민감한 코드는 AI가 작성한 결과를 그대로 운영 환경에 넣지 않는 편이 안전합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cursor나 Claude Code를 사용하고 있다면, 자주 쓰는 프레임워크부터 Context7을 적용해보는 것이 좋습니다. 특히 Next.js, Supabase, Cloudflare처럼 문서 변화가 빠른 도구부터 테스트해보면 차이를 체감하기 쉽습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;정리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context7은 AI 코딩 도구가 최신 개발 문서를 참고하도록 도와주는 MCP 기반 도구입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 오래된 학습 데이터에만 의존하면, 최신 라이브러리와 맞지 않는 코드가 나올 수 있습니다. Context7은 관련 문서와 코드 예제를 AI의 컨텍스트에 넣어 이런 문제를 줄이는 데 도움을 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 단순합니다.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;질문 + 구체적인 기술 조건 + use context7&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;이 조합을 사용하면 AI가 더 최신 문서에 가까운 답변을 만들 가능성이 높아집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 Context7이 모든 문제를 해결하는 것은 아닙니다.&lt;br /&gt;프로젝트 구조, 보안 요구사항, 팀 규칙, 실제 실행 결과는 개발자가 직접 확인해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 도구를 실무에 쓰고 있다면 Context7은 한 번쯤 적용해볼 만한 도구입니다. 특히 변화가 빠른 프레임워크와 SDK를 다룰 때, AI 답변의 기준점을 최신 문서 쪽으로 옮겨주는 역할을 합니다.&lt;/p&gt;</description>
      <category>AI/AI 코딩</category>
      <category>AI에이전트</category>
      <category>AI코딩</category>
      <category>claudecode</category>
      <category>Context7</category>
      <category>CURSOR</category>
      <category>MCP</category>
      <category>개발문서</category>
      <category>코딩자동화</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/40</guid>
      <comments>https://notebase.tistory.com/entry/what-is-context7-mcp-ai-coding-docs#entry40comment</comments>
      <pubDate>Fri, 22 May 2026 20:30:24 +0900</pubDate>
    </item>
    <item>
      <title>MCP란 무엇인가? AI 에이전트가 외부 도구와 연결되는 방식</title>
      <link>https://notebase.tistory.com/entry/what-is-mcp-ai-agent-tool-connection</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;MCP(Model Context Protocol)가 무엇인지, AI 에이전트가 외부 도구와 데이터를 어떻게 연결하는지 초보자도 이해할 수 있게 정리합니다.&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;MCP란 AI 에이전트가 외부 도구, 데이터, 앱과 연결될 수 있도록 만든 표준 프로토콜이다. 정식 명칭은 &lt;b&gt;Model Context Protocol&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말하면, AI가 혼자 대화만 하는 수준을 넘어 파일을 읽고, 데이터베이스를 조회하고, 개발 도구를 다루고, 업무 앱과 연결될 수 있게 해주는 연결 규칙이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Anthropic 공식 문서에서는 MCP를 &amp;ldquo;AI 애플리케이션을 위한 USB-C 포트&amp;rdquo;에 비유한다. USB-C가 여러 기기와 주변기기를 하나의 방식으로 연결하듯, MCP는 AI 모델과 다양한 데이터&amp;middot;도구를 표준화된 방식으로 연결한다.&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;MCP는 왜 등장했을까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 AI 챗봇은 기본적으로 대화창 안에서만 작동했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 질문하면 AI는 학습된 지식이나 입력된 문맥을 바탕으로 답변했다. 하지만 실제 업무에서는 이것만으로 부족하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 AI에게 이런 일을 시키고 싶을 수 있다.&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;GitHub 이슈 확인하기&lt;/li&gt;
&lt;li&gt;Notion 문서 요약하기&lt;/li&gt;
&lt;li&gt;데이터베이스에서 고객 정보 조회하기&lt;/li&gt;
&lt;li&gt;Slack 메시지 기반으로 할 일 정리하기&lt;/li&gt;
&lt;li&gt;코드 에디터에서 프로젝트 구조 분석하기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 작업을 하려면 AI가 외부 시스템과 연결되어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 서비스마다 연결 방식이 다르다는 점이다. GitHub, Slack, Google Drive, 데이터베이스, 로컬 파일 시스템은 모두 다른 API와 인증 방식을 가진다. AI 앱마다 각각 별도 연동을 만들면 개발 비용이 커지고, 유지보수도 어려워진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP는 이 문제를 줄이기 위해 만들어졌다. Anthropic은 MCP를 AI 시스템과 데이터 소스를 연결하기 위한 개방형 표준으로 소개했고, 2024년 11월 처음 공개했다.&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;AI 에이전트와 MCP의 관계&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트는 단순히 답변만 하는 AI가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목표를 이해하고, 필요한 단계를 나누고, 외부 도구를 사용해 작업을 수행하는 AI에 가깝다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 사용자가 이렇게 요청했다고 해보자.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;지난주 회의록을 찾아서 핵심 결정사항을 정리하고, 관련 GitHub 이슈까지 확인해줘.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 챗봇이라면 사용자가 회의록과 GitHub 내용을 직접 붙여넣어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 MCP로 외부 도구가 연결된 AI 에이전트라면 흐름이 달라질 수 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;회의록이 저장된 위치를 찾는다.&lt;/li&gt;
&lt;li&gt;문서를 읽는다.&lt;/li&gt;
&lt;li&gt;결정사항을 요약한다.&lt;/li&gt;
&lt;li&gt;GitHub 이슈를 조회한다.&lt;/li&gt;
&lt;li&gt;회의 내용과 이슈를 연결해 정리한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 파일 시스템 MCP 서버가 연결되어 있다면, AI는 허용된 범위 안에서 &lt;code&gt;meeting-notes/2026-05.md&lt;/code&gt; 같은 문서를 찾아 읽고 요약할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 MCP는 AI가 직접 외부 세계에 접근할 수 있도록 돕는 연결 계층이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, AI 에이전트가 일하는 사람이라면 MCP는 그 사람이 업무 도구에 접근하는 표준 인터페이스에 가깝다.&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;MCP는 어떻게 외부 도구와 연결될까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP는 기본적으로 &lt;b&gt;클라이언트-서버 구조&lt;/b&gt;를 따른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 MCP 소개 문서에서도 핵심 구조를 MCP Host, MCP Client, MCP Server, 로컬 데이터 소스, 원격 서비스로 설명한다.&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;MCP Host, Client, Server 구조&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구조를 쉽게 풀면 다음과 같다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구성 요소&lt;/th&gt;
&lt;th&gt;역할&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MCP Host&lt;/td&gt;
&lt;td&gt;AI가 실행되는 앱. 예: Claude Desktop, IDE, AI 도구&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MCP Client&lt;/td&gt;
&lt;td&gt;MCP 서버와 연결을 유지하는 클라이언트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MCP Server&lt;/td&gt;
&lt;td&gt;특정 도구나 데이터 소스를 AI에게 노출하는 프로그램&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local Data Source&lt;/td&gt;
&lt;td&gt;내 컴퓨터의 파일, 로컬 DB 등&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remote Service&lt;/td&gt;
&lt;td&gt;GitHub, Slack, Notion 같은 외부 서비스&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 &lt;b&gt;MCP 서버&lt;/b&gt;다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP 서버는 특정 도구를 AI가 사용할 수 있는 형태로 포장해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 GitHub MCP 서버가 있다면, AI는 GitHub의 이슈, PR, 저장소 정보를 정해진 방식으로 요청할 수 있다. 파일 시스템 MCP 서버가 있다면, AI는 허용된 범위 안에서 파일을 읽거나 검색할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 모델이 모든 서비스의 API 사용법을 직접 알아야 하는 것이 아니다. MCP 서버가 중간에서 &amp;ldquo;이 도구는 이런 기능을 제공한다&amp;rdquo;고 알려주는 구조다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;MCP 서버와 MCP 클라이언트는 무엇인가?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP를 이해할 때 가장 헷갈리는 부분이 서버와 클라이언트다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 웹 서비스에서는 사용자가 브라우저를 열고 서버에 접속한다. MCP에서도 비슷하게 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 앱이 MCP 클라이언트 역할을 하고, 외부 도구를 감싼 프로그램이 MCP 서버 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Claude Desktop이나 Cursor 같은 도구가 MCP를 지원한다고 해보자. 사용자가 파일 시스템 MCP 서버를 연결하면, AI 앱은 그 서버를 통해 로컬 파일 관련 기능을 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 다음과 같다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;쉽게 말하면&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MCP 클라이언트&lt;/td&gt;
&lt;td&gt;AI 앱 안에서 MCP 서버와 통신하는 쪽&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MCP 서버&lt;/td&gt;
&lt;td&gt;외부 도구나 데이터를 AI가 쓸 수 있게 열어주는 쪽&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;도구&lt;/td&gt;
&lt;td&gt;AI가 실제로 호출하는 기능. 예: 파일 읽기, 이슈 조회, 검색&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;이 구조 덕분에 AI 앱은 모든 도구를 직접 내장하지 않아도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 MCP 서버만 연결하면 된다.&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;MCP 서버가 제공하는 3가지 요소&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP 서버는 단순히 외부 서비스를 연결하는 통로만 제공하지 않는다. 보통 다음과 같은 요소를 AI 클라이언트에 노출한다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;요소&lt;/th&gt;
&lt;th&gt;의미&lt;/th&gt;
&lt;th&gt;예시&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Resources&lt;/td&gt;
&lt;td&gt;AI가 읽을 수 있는 데이터나 문서&lt;/td&gt;
&lt;td&gt;회의록 파일, 데이터베이스 조회 결과, 문서 내용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tools&lt;/td&gt;
&lt;td&gt;AI가 직접 호출할 수 있는 기능&lt;/td&gt;
&lt;td&gt;파일 검색, GitHub 이슈 조회, 테스트 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prompts&lt;/td&gt;
&lt;td&gt;자주 쓰는 프롬프트나 작업 템플릿&lt;/td&gt;
&lt;td&gt;코드 리뷰 요청 양식, 회의록 요약 템플릿&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 중 &lt;b&gt;Tools&lt;/b&gt;는 AI 에이전트와 특히 밀접하다. AI가 단순히 정보를 읽는 데 그치지 않고, 필요한 기능을 호출해 작업을 진행할 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 &lt;b&gt;Resources&lt;/b&gt;는 AI가 참고할 수 있는 문맥을 제공하는 데 가깝다. 예를 들어 특정 문서, 파일 내용, 데이터 조회 결과를 AI가 읽을 수 있게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Prompts&lt;/b&gt;는 반복적으로 쓰는 작업 방식을 템플릿처럼 제공하는 요소다. 사용자가 매번 긴 지시문을 작성하지 않아도, 미리 정의된 흐름을 바탕으로 작업을 시작할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;API와 MCP는 무엇이 다를까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP를 처음 들으면 &amp;ldquo;그냥 API랑 같은 거 아닌가?&amp;rdquo;라고 생각할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비슷한 부분은 있다. 둘 다 시스템 사이의 연결을 다룬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 관점이 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API는 보통 특정 서비스가 외부 개발자에게 기능을 제공하는 방식이다. 예를 들어 GitHub API, Slack API, Google Drive API처럼 서비스마다 사용법이 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP는 AI 애플리케이션이 여러 도구를 일관된 방식으로 발견하고 사용할 수 있게 만드는 프로토콜이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비유하면 API는 각 가게의 개별 출입문이고, MCP는 AI가 여러 가게에 들어갈 때 사용할 수 있는 공통 출입 방식에 가깝다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;API&lt;/th&gt;
&lt;th&gt;MCP&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;목적&lt;/td&gt;
&lt;td&gt;특정 서비스 기능 제공&lt;/td&gt;
&lt;td&gt;AI와 외부 도구 연결 표준화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;사용 주체&lt;/td&gt;
&lt;td&gt;개발자, 서버, 앱&lt;/td&gt;
&lt;td&gt;AI 앱, AI 에이전트&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;연결 방식&lt;/td&gt;
&lt;td&gt;서비스마다 다름&lt;/td&gt;
&lt;td&gt;공통 프로토콜 기반&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;예시&lt;/td&gt;
&lt;td&gt;GitHub API 직접 호출&lt;/td&gt;
&lt;td&gt;GitHub MCP 서버를 통해 AI가 사용&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;MCP가 API를 대체한다기보다는, 기존 API를 AI가 사용하기 좋은 형태로 감싸는 역할에 가깝다.&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;플러그인과 MCP는 무엇이 다를까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP는 AI 플러그인과도 비교된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플러그인은 특정 AI 서비스에 붙는 확장 기능으로 이해할 수 있다. 예를 들어 어떤 챗봇에만 작동하는 전용 확장 기능이 있을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 MCP는 특정 앱 하나에만 묶이기보다, 여러 AI 앱과 도구 사이에서 사용할 수 있는 표준을 지향한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 차이가 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플러그인 방식은 편하지만, 특정 플랫폼에 종속되기 쉽다. 반면 MCP 방식은 같은 MCP 서버를 여러 AI 클라이언트에서 활용할 가능성이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 MCP는 Claude뿐 아니라 ChatGPT, Cursor, Gemini, Microsoft Copilot, Visual Studio Code 등 주요 AI 제품과 개발 환경으로 확산되었다고 Anthropic은 2025년 말 발표했다.&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;MCP를 사용하면 무엇이 좋아질까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP의 가장 큰 장점은 연결 방식의 표준화다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 앱마다 도구 연동을 따로 만들 필요가 줄어든다. MCP 서버를 만들면 MCP를 지원하는 여러 클라이언트에서 재사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자 입장에서는 다음과 같은 장점이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;같은 도구 연동을 여러 AI 앱에서 활용 가능&lt;/li&gt;
&lt;li&gt;로컬 파일, DB, API 연동 구조를 표준화 가능&lt;/li&gt;
&lt;li&gt;AI 에이전트 개발 시 도구 연결 비용 감소&lt;/li&gt;
&lt;li&gt;Claude, Cursor, VS Code 같은 개발 환경과 연결 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 입장에서는 AI가 더 실질적인 작업을 할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 &amp;ldquo;방법을 알려주는 AI&amp;rdquo;에서 &amp;ldquo;도구를 사용해 작업을 도와주는 AI&amp;rdquo;로 바뀌는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 개발자는 코드베이스를 분석하는 AI 에이전트를 만들 수 있고, 회사는 내부 문서 검색이나 업무 자동화에 MCP를 활용할 수 있다.&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;MCP가 중요한 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP가 주목받는 이유는 AI 사용 방식이 바뀌고 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전의 AI는 주로 질문에 답하는 도구였다. 하지만 AI 에이전트는 점점 실제 작업 흐름 안으로 들어오고 있다.&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;ldquo;AI 모델이 얼마나 똑똑한가&amp;rdquo;만이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 어떤 도구에 접근할 수 있는지, 어떤 권한으로 접근하는지, 그 연결이 얼마나 안정적이고 안전한지도 중요해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP는 이 연결 방식을 표준화하려는 시도다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2025년 12월에는 Anthropic이 MCP를 Linux Foundation 산하 Agentic AI Foundation에 기부한다고 발표했다. Linux Foundation도 AAIF 출범과 함께 MCP, goose, AGENTS.md 같은 프로젝트를 주요 기여 프로젝트로 소개했다. 이는 MCP가 특정 회사의 단독 기술이 아니라, 더 중립적인 에이전트 AI 생태계 표준으로 발전하려는 흐름으로 볼 수 있다.&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;MCP를 사용할 때 주의할 점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP가 편리하다고 해서 아무 서버나 연결해도 되는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP 서버는 AI에게 외부 도구 접근 권한을 열어주는 역할을 한다. 파일 시스템, 데이터베이스, GitHub, 사내 문서 같은 민감한 자원과 연결될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 보안 설정이 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 다음 항목은 반드시 확인해야 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MCP 서버가 어떤 권한을 요구하는가&lt;/li&gt;
&lt;li&gt;로컬 파일 접근 범위가 어디까지인가&lt;/li&gt;
&lt;li&gt;인증 토큰이나 API 키가 안전하게 관리되는가&lt;/li&gt;
&lt;li&gt;신뢰할 수 있는 MCP 서버인가&lt;/li&gt;
&lt;li&gt;AI가 실행할 수 있는 작업 범위가 제한되어 있는가&lt;/li&gt;
&lt;li&gt;로그와 실행 기록을 확인할 수 있는가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트는 사용자의 요청을 해석해 도구를 호출한다. 이 과정에서 잘못된 프롬프트, 악의적인 문서, 불필요하게 넓은 권한이 결합되면 보안 문제가 생길 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP 보안과 유지보수에 관한 연구들도 MCP 서버의 취약점, 도구 오염, 권한 검증 문제 등을 지적하고 있다. 따라서 실제 업무 환경에서는 최소 권한 원칙, 신뢰할 수 있는 서버 사용, 실행 로그 관리가 필요하다.&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;MCP는 개발자에게만 필요한 개념일까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 MCP는 개발자 도구에서 특히 많이 언급된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude Desktop, Claude Code, Cursor, VS Code, 로컬 파일 시스템, GitHub 연동 같은 사례가 많기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 MCP의 의미는 개발자에게만 한정되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 AI 에이전트가 업무 도구, 문서 관리, 데이터 분석, 고객 지원, 자동화 시스템과 연결될수록 MCP 같은 연결 표준은 더 중요해질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비개발자라도 MCP의 개념을 이해해두면 AI 도구를 볼 때 다음 질문을 할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 AI는 어떤 외부 도구와 연결되는가?&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;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, MCP는 단순한 개발 용어가 아니라 AI 에이전트 시대의 연결 방식에 관한 개념이다.&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;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP는 Model Context Protocol의 약자다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트가 외부 도구, 데이터, 앱과 연결될 수 있도록 만든 표준 프로토콜이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에는 AI 앱마다 도구 연동을 따로 만들어야 했다. MCP는 이 연결 방식을 표준화해 AI가 파일, 데이터베이스, API, 업무 도구를 더 일관된 방식으로 사용할 수 있게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심 구조는 MCP Host, MCP Client, MCP Server로 나눌 수 있다. 이 중 MCP 서버는 특정 도구나 데이터를 AI가 사용할 수 있는 형태로 제공하는 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP 서버는 Resources, Tools, Prompts 같은 요소를 통해 AI에게 읽을 자료, 실행할 기능, 반복 가능한 작업 템플릿을 제공할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP가 중요한 이유는 AI가 단순한 챗봇에서 실제 작업을 수행하는 에이전트로 바뀌고 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 MCP는 외부 도구 접근 권한을 다루기 때문에 보안 설정이 중요하다. 신뢰할 수 없는 MCP 서버를 연결하거나, 지나치게 넓은 권한을 주는 방식은 피해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트를 제대로 이해하려면 모델 성능뿐 아니라 &amp;ldquo;AI가 외부 도구와 어떻게 연결되는가&amp;rdquo;도 함께 봐야 한다. 그 중심에 있는 대표적인 표준이 MCP다.&lt;/p&gt;</description>
      <category>AI/AI 코딩</category>
      <category>AI도구</category>
      <category>AI에이전트</category>
      <category>AI자동화</category>
      <category>chatGPT</category>
      <category>Claude</category>
      <category>MCP</category>
      <category>model context protocol</category>
      <category>개발도구</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/39</guid>
      <comments>https://notebase.tistory.com/entry/what-is-mcp-ai-agent-tool-connection#entry39comment</comments>
      <pubDate>Fri, 22 May 2026 19:53:02 +0900</pubDate>
    </item>
    <item>
      <title>AI 코딩 에이전트란? 코드 자동완성과 무엇이 다를까</title>
      <link>https://notebase.tistory.com/entry/ai-coding-agent-vs-code-autocomplete</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 에이전트가 무엇인지, 기존 코드 자동완성과 어떤 차이가 있는지 초보자도 이해할 수 있게 정리했습니다. 실제 개발 업무에서 맡길 수 있는 일과 주의할 점까지 함께 설명합니다.&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;AI 코딩 에이전트는 단순히 코드를 추천하는 도구가 아닙니다. 개발자가 목표를 주면 코드베이스를 읽고, 파일을 수정하고, 테스트를 실행하며, 경우에 따라 PR까지 제안하는 AI 개발 도구에 가깝습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 코드 자동완성이 &amp;ldquo;다음 줄을 추천하는 도구&amp;rdquo;였다면, AI 코딩 에이전트는 &amp;ldquo;작업을 맡길 수 있는 도구&amp;rdquo;에 가깝습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2026년 5월 기준으로 Claude Code, Cursor Agent, OpenAI Codex, GitHub Copilot coding agent 같은 도구들이 이 흐름을 대표합니다. 이들은 단순히 코드를 제안하는 수준을 넘어, 코드베이스를 탐색하고 여러 파일을 수정하며 테스트나 명령어 실행까지 연결되는 방식으로 발전하고 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AI 코딩 에이전트란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 에이전트는 개발자가 자연어로 지시한 작업을 이해하고, 코드 작업을 여러 단계로 나누어 수행하는 AI 도구입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 개발자가 이렇게 요청할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인 실패 시 에러 메시지가 너무 불친절해.&lt;br /&gt;프론트엔드 화면과 API 응답 처리를 확인해서 사용자에게 더 명확한 메시지가 나오도록 수정해줘.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 코드 자동완성 도구라면 현재 작성 중인 파일 안에서 다음 코드를 추천하는 수준에 머무는 경우가 많습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 AI 코딩 에이전트는 보통 다음과 같은 흐름으로 움직입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&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;li&gt;필요한 경우 테스트나 빌드 명령어를 실행한다.&lt;/li&gt;
&lt;li&gt;오류가 나면 다시 수정한다.&lt;/li&gt;
&lt;li&gt;변경 내용을 개발자가 검토할 수 있게 정리한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, AI 코딩 에이전트의 핵심은 &lt;b&gt;코드 생성 자체가 아니라 작업 수행&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 AI 코딩 에이전트가 주목받는 배경에는 LLM이 한 번에 참고할 수 있는 정보량, 즉 &lt;b&gt;컨텍스트 윈도우(Context Window)&lt;/b&gt; 확대도 있습니다. 여기에 코드 검색, 파일 탐색, RAG 같은 방식이 결합되면서 하나의 파일이 아니라 여러 파일의 관계를 파악하고 수정하는 흐름이 가능해졌습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 컨텍스트 윈도우가 커졌다고 해서 AI가 프로젝트 전체를 항상 정확히 이해한다는 뜻은 아닙니다. 관련 없는 파일을 잘못 참고하거나, 오래된 패턴을 따라가거나, 프로젝트의 숨은 규칙을 놓칠 수 있습니다. 그래서 에이전트형 도구일수록 &lt;b&gt;명확한 지시와 결과 검토&lt;/b&gt;가 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;코드 자동완성과 무엇이 다를까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 자동완성은 개발자가 코드를 작성하는 순간에 도움을 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 함수 이름을 입력하면 이어질 코드를 추천하거나, 반복적인 패턴을 자동으로 채워줍니다. 개발자가 직접 방향을 잡고, AI는 그 옆에서 빠르게 보조하는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 에이전트는 한 단계 더 나아갑니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자가 &amp;ldquo;이 기능을 추가해줘&amp;rdquo;, &amp;ldquo;이 버그를 찾아서 고쳐줘&amp;rdquo;, &amp;ldquo;이 구조를 리팩토링해줘&amp;rdquo;처럼 목표를 주면, AI가 코드베이스 안에서 필요한 작업을 찾아 진행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 차이는 다음과 같습니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;코드 자동완성&lt;/th&gt;
&lt;th&gt;AI 코딩 에이전트&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;역할&lt;/td&gt;
&lt;td&gt;작성 중인 코드 보조&lt;/td&gt;
&lt;td&gt;개발 작업 단위 수행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;작업 범위&lt;/td&gt;
&lt;td&gt;한 줄, 함수, 파일 중심&lt;/td&gt;
&lt;td&gt;여러 파일, 테스트, PR까지 확장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;사용 방식&lt;/td&gt;
&lt;td&gt;개발자가 직접 입력하며 추천 받음&lt;/td&gt;
&lt;td&gt;목표를 설명하고 작업을 위임&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;장점&lt;/td&gt;
&lt;td&gt;빠르고 가볍다&lt;/td&gt;
&lt;td&gt;복잡한 작업을 맡길 수 있다&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;주의점&lt;/td&gt;
&lt;td&gt;맥락이 좁을 수 있다&lt;/td&gt;
&lt;td&gt;변경 범위가 커서 검토가 중요하다&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;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;br /&gt;AI 코딩 에이전트에서는 개발자가 목표와 기준을 제시하고, AI가 중간 작업을 수행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 &lt;b&gt;최종 책임은 여전히 개발자에게 있습니다.&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 코드 생성 능력이 아니라 작업 방식의 변화입니다. 코드 자동완성은 개발자의 입력을 보조하고, AI 코딩 에이전트는 목표 단위의 작업을 수행합니다.&lt;/p&gt;
&lt;/blockquote&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;ldquo;에이전트&amp;rdquo;라고 부를까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 에이전트라는 말은 단순한 챗봇과 구분하기 위해 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;챗봇은 질문에 답하거나 코드를 보여주는 데 그칠 수 있습니다. 하지만 에이전트는 도구를 사용해 작업을 진행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 에이전트는 보통 다음과 같은 도구 사용 능력을 가집니다.&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;li&gt;테스트 실행&lt;/li&gt;
&lt;li&gt;빌드 결과 확인&lt;/li&gt;
&lt;li&gt;Git 변경 사항 정리&lt;/li&gt;
&lt;li&gt;PR 생성 또는 제안&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 점에서 AI 코딩 에이전트는 &amp;ldquo;코드를 알려주는 AI&amp;rdquo;라기보다 &lt;b&gt;개발 환경 안에서 작업하는 AI&lt;/b&gt;에 가깝습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AI 코딩 에이전트가 할 수 있는 일&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 에이전트가 잘 맞는 작업은 비교적 목표가 분명하고, 결과를 검증할 수 있는 개발 작업입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 다음과 같습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 작은 기능 추가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 관리자 페이지에 검색 필터를 추가하거나, API 응답 필드를 화면에 표시하는 작업이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 작업은 관련 파일이 여러 개일 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;API 타입 정의&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;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 자동완성은 각 파일을 수정할 때 도움을 줄 수 있습니다.&lt;br /&gt;하지만 AI 코딩 에이전트는 관련 파일을 찾아 한 번에 작업 흐름을 구성할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &lt;b&gt;여러 파일 수정&lt;/b&gt;이 필요한 작업에서는 자동완성보다 에이전트 방식이 더 자연스러울 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 버그 수정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러 로그나 재현 조건을 주면 AI 코딩 에이전트가 관련 코드를 찾아 원인을 추정하고 수정안을 만들 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 이 부분은 반드시 사람이 검토해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 테스트를 통과시켰다고 해서 비즈니스 로직까지 정확히 이해했다고 볼 수는 없습니다. 특히 결제, 인증, 보안, 권한 처리처럼 영향이 큰 영역은 더 조심해야 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 리팩토링&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 에이전트는 반복적인 리팩토링에 유용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 작업입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오래된 함수명을 새 이름으로 변경&lt;/li&gt;
&lt;li&gt;중복 로직을 공통 함수로 분리&lt;/li&gt;
&lt;li&gt;타입 정의 정리&lt;/li&gt;
&lt;li&gt;폴더 구조 이동&lt;/li&gt;
&lt;li&gt;테스트 코드 보강&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 작업은 사람이 하면 단순 반복이 많지만, 실수하면 사이드 이펙트가 생기기 쉽습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트에게 맡길 때는 범위를 좁히는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 예시는 다음과 같습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;userProfile&lt;/code&gt; 관련 컴포넌트 안에서만 중복된 날짜 포맷팅 로직을 공통 함수로 분리해줘.&lt;br /&gt;다른 화면의 동작은 바꾸지 말고, 기존 테스트가 깨지지 않는지 확인해줘.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나쁜 예시는 다음과 같습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 프로젝트 구조를 깔끔하게 바꿔줘.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청이 넓을수록 AI가 불필요한 변경을 만들 가능성이 커집니다.&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;4. 테스트 코드 작성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 에이전트는 기존 코드와 테스트 스타일을 읽고 비슷한 방식으로 테스트를 추가하는 데 도움을 줄 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 요청이 가능합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;paymentService&lt;/code&gt;의 예외 케이스 테스트가 부족해.&lt;br /&gt;기존 테스트 스타일을 유지해서 실패 응답, 타임아웃, 잘못된 입력 케이스를 추가해줘.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트는 AI 에이전트와 궁합이 좋은 편입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이유는 결과를 비교적 명확하게 확인할 수 있기 때문입니다. 테스트가 통과하는지, 커버해야 할 조건이 포함됐는지 개발자가 검토하기 쉽습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;코드 자동완성이 여전히 유용한 경우&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 에이전트가 등장했다고 해서 코드 자동완성이 필요 없어지는 것은 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오히려 두 도구는 쓰임새가 다릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 자동완성은 다음 상황에서 여전히 유용합니다.&lt;/p&gt;
&lt;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;li&gt;개발자가 직접 흐름을 통제하고 싶은 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 배열을 필터링하거나, DTO를 변환하거나, UI 이벤트 핸들러를 작성하는 정도라면 에이전트까지 쓸 필요가 없을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 작업은 코드 자동완성이 더 빠르고 자연스럽습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 AI 코딩 에이전트는 작업 단위가 커질수록 유용합니다.&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;li&gt;PR 단위로 작업을 나누고 싶을 때&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단히 말하면, &lt;b&gt;코드 자동완성은 손을 빠르게 해주고, AI 코딩 에이전트는 작업 흐름을 줄여줍니다.&lt;/b&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AI 코딩 에이전트 사용 시 주의할 점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 에이전트를 사용할 때 가장 중요한 것은 &lt;b&gt;검토 책임&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에이전트가 코드를 수정하고 테스트까지 실행할 수 있다고 해도, 그 결과가 항상 안전하다는 뜻은 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 다음 부분은 사람이 직접 확인해야 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 변경 범위가 과도하지 않은가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트는 요청을 해결하려다 예상보다 많은 파일을 수정할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 버그 수정 요청이었는데 폴더 구조를 바꾸거나, 관련 없는 코드 스타일을 함께 수정하는 경우도 생길 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 작업을 맡길 때는 범위를 제한하는 것이 좋습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일과 관련 테스트만 수정해줘.&lt;br /&gt;공개 API 스펙은 변경하지 마.&lt;br /&gt;UI 문구 외의 동작은 바꾸지 마.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 조건을 함께 주면 결과가 더 안정적입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 테스트 통과가 정답은 아니다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트가 부족한 프로젝트에서는 AI가 만든 코드가 테스트를 통과해도 실제 서비스에서는 문제가 생길 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트가 검증하지 않는 영역은 AI도 놓치기 쉽습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 에이전트에게 작업을 맡기기 전에 다음을 확인하는 편이 좋습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;핵심 로직 테스트가 있는가&lt;/li&gt;
&lt;li&gt;실패 케이스 테스트가 있는가&lt;/li&gt;
&lt;li&gt;권한, 보안, 결제 관련 테스트가 있는가&lt;/li&gt;
&lt;li&gt;변경 후 사람이 직접 확인해야 할 시나리오가 정리되어 있는가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;테스트 통과가 정답은 아닙니다.&lt;/b&gt;&lt;br /&gt;테스트가 확인하지 않는 부분은 여전히 개발자가 직접 봐야 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 보안 정보 노출에 주의해야 한다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 에이전트가 코드베이스, 터미널, 외부 도구와 연결될수록 권한 관리가 중요해집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API 키, 인증 토큰, 고객 정보, 내부 인프라 정보가 노출되지 않도록 주의해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 회사 프로젝트에서는 다음을 확인해야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어떤 저장소에 접근할 수 있는지&lt;/li&gt;
&lt;li&gt;어떤 명령어를 실행할 수 있는지&lt;/li&gt;
&lt;li&gt;인터넷 접근이 허용되는지&lt;/li&gt;
&lt;li&gt;비밀 정보가 프롬프트나 로그에 포함되지 않는지&lt;/li&gt;
&lt;li&gt;생성된 코드가 라이선스나 보안 정책을 위반하지 않는지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 에이전트는 강력한 도구일수록 접근 권한도 중요해집니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 아키텍처 판단은 개발자가 해야 한다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI는 코드를 빠르게 수정할 수 있지만, 제품의 방향이나 시스템 설계 의도를 완전히 이해한다고 보기 어렵습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 판단은 개발자가 주도해야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 기능을 어디에 배치할 것인가&lt;/li&gt;
&lt;li&gt;기존 구조를 유지할 것인가, 바꿀 것인가&lt;/li&gt;
&lt;li&gt;성능과 유지보수성 중 무엇을 우선할 것인가&lt;/li&gt;
&lt;li&gt;보안상 허용 가능한 방식인가&lt;/li&gt;
&lt;li&gt;장기적으로 기술 부채가 늘어나지 않는가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 에이전트는 실행 도구로는 유용하지만, &lt;b&gt;설계 책임&lt;/b&gt;까지 넘기기는 어렵습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 에이전트는 강력한 개발 보조 도구이지만, 변경 범위와 결과의 안전성은 여전히 개발자가 판단해야 합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AI 코딩 에이전트를 잘 쓰는 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 에이전트를 잘 쓰려면 요청을 구체적으로 작성해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;막연한 요청보다 조건이 있는 요청이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음처럼 작성할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회원가입 폼에서 이메일 형식 검증을 추가해줘.&lt;br /&gt;기존 UI 스타일은 유지하고, 검증 실패 시 입력창 아래에 에러 메시지를 보여줘.&lt;br /&gt;관련 테스트가 있다면 함께 수정하고, 없으면 최소한의 테스트를 추가해줘.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 요청에는 목표, 범위, UI 조건, 테스트 조건이 포함되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 다음 요청은 위험합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회원가입 기능 개선해줘.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무엇을 개선해야 하는지 불명확합니다.&lt;br /&gt;AI가 임의로 판단할 여지가 커집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 요청에는 보통 다음 요소가 들어갑니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해결하고 싶은 문제&lt;/li&gt;
&lt;li&gt;수정해도 되는 범위&lt;/li&gt;
&lt;li&gt;수정하면 안 되는 범위&lt;/li&gt;
&lt;li&gt;따라야 할 코드 스타일&lt;/li&gt;
&lt;li&gt;테스트 실행 방법&lt;/li&gt;
&lt;li&gt;완료 기준&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트에 따라 &lt;code&gt;AGENTS.md&lt;/code&gt;, &lt;code&gt;CLAUDE.md&lt;/code&gt; 같은 지침 파일을 두는 경우도 있습니다. 이런 파일은 에이전트가 매번 같은 기준으로 작업하도록 돕는 역할을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;# AI coding agent instructions

- 기존 코드 스타일을 유지한다.
- 수정 범위는 요청받은 기능과 관련 파일로 제한한다.
- 테스트 명령어는 `npm test`를 사용한다.
- API 응답 형식은 변경하지 않는다.
- 보안 관련 파일은 사용자 확인 없이 수정하지 않는다.
- 대규모 리팩토링이 필요하면 먼저 변경 계획을 제안한다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 지침 파일이 있으면 에이전트가 프로젝트의 기본 규칙을 더 잘 따를 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 지침 파일이 모든 문제를 해결하지는 않습니다. 그래도 테스트 명령어, 수정 금지 영역, 코드 스타일 같은 반복 규칙을 명시해 두면 매번 같은 설명을 반복하지 않아도 됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;개발자는 앞으로 무엇을 해야 할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 에이전트가 발전하면 개발자의 역할은 단순 코딩에서 점점 더 넓어질 가능성이 큽니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;문제를 정확히 정의하는 능력&lt;/li&gt;
&lt;li&gt;작업 범위를 나누는 능력&lt;/li&gt;
&lt;li&gt;AI가 만든 변경 사항을 리뷰하는 능력&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;AI 코딩 에이전트는 개발자를 대체한다기보다, 개발자가 다루는 작업 단위를 바꾸고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에는 개발자가 직접 코드를 작성하는 시간이 많았다면, 이제는 AI에게 작업을 맡기고 결과를 검토하는 시간이 늘어날 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다고 개발 지식이 덜 중요해지는 것은 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오히려 AI가 만든 코드가 맞는지 판단하려면 더 정확한 이해가 필요합니다.&lt;br /&gt;기초가 부족하면 AI가 만든 결과를 그대로 받아들이게 되고, 그만큼 위험도 커집니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 코딩 에이전트는 코드 자동완성의 확장판처럼 보이지만, 실제로는 사용 방식이 다릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 자동완성은 개발자가 작성 중인 코드를 빠르게 완성하도록 돕습니다.&lt;br /&gt;AI 코딩 에이전트는 개발자가 맡긴 작업을 코드베이스 안에서 직접 수행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 둘 중 하나만 선택해야 하는 관계는 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;짧고 명확한 코드는 자동완성이 편합니다.&lt;br /&gt;여러 파일을 수정하거나 테스트까지 확인해야 하는 작업은 AI 코딩 에이전트가 유용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 에이전트가 강력해질수록 검토 책임도 커집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI에게 코딩을 맡길 수는 있지만, 무엇을 만들지, 어디까지 바꿀지, 결과가 안전한지는 여전히 개발자가 판단해야 합니다.&lt;/p&gt;</description>
      <category>AI/AI 코딩</category>
      <category>ai코딩도구</category>
      <category>AI코딩에이전트</category>
      <category>claudecode</category>
      <category>codex</category>
      <category>CURSOR</category>
      <category>githubcopilot</category>
      <category>llm</category>
      <category>개발자도구</category>
      <category>코드자동완성</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/38</guid>
      <comments>https://notebase.tistory.com/entry/ai-coding-agent-vs-code-autocomplete#entry38comment</comments>
      <pubDate>Fri, 22 May 2026 19:02:24 +0900</pubDate>
    </item>
    <item>
      <title>HBM이란? 초보자도 이해하는 고대역폭 메모리 개념</title>
      <link>https://notebase.tistory.com/entry/what-is-hbm-memory</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;HBM이란 고대역폭 메모리로, 여러 개의 D램을 수직으로 쌓아 GPU와 AI 반도체에 빠르게 데이터를 전달하는 메모리입니다. HBM의 뜻, 원리, D램과의 차이, 장단점을 초보자 기준으로 쉽게 정리합니다.&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;HBM이란 High Bandwidth Memory의 줄임말로, 한국어로는 고대역폭 메모리라고 부릅니다. AI 반도체와 GPU가 많은 데이터를 빠르게 처리할 수 있도록 돕는 고성능 메모리입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뉴스에서 HBM이라는 단어가 자주 나오는 이유는 간단합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 발전할수록 연산해야 할 데이터가 많아지고, 그 데이터를 빠르게 공급해 줄 메모리가 중요해졌기 때문입니다. GPU가 아무리 빠르게 계산할 수 있어도, 필요한 데이터를 제때 받지 못하면 성능을 제대로 내기 어렵습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HBM은 이 문제를 줄이기 위해 등장한 메모리입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;HBM 뜻부터 이해하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HBM은 &lt;b&gt;High Bandwidth Memory&lt;/b&gt;의 약자입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 단어는 &lt;code&gt;Bandwidth&lt;/code&gt;, 즉 &lt;b&gt;대역폭&lt;/b&gt;입니다. 대역폭은 한 번에 데이터를 얼마나 많이 보낼 수 있는지를 뜻합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말하면 도로의 차선 수와 비슷합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;차선이 좁으면 차가 많이 막힙니다.&lt;/li&gt;
&lt;li&gt;차선이 넓으면 더 많은 차가 동시에 지나갈 수 있습니다.&lt;/li&gt;
&lt;li&gt;메모리도 마찬가지로 데이터가 지나가는 길이 넓을수록 더 많은 데이터를 빠르게 보낼 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HBM은 이 데이터 통로를 크게 넓힌 메모리라고 보면 됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;HBM은 왜 빠른 메모리라고 불릴까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 메모리는 메모리 칩을 기판 위에 옆으로 배치하는 방식이 많습니다. 반면 HBM은 여러 개의 D램 칩을 위로 쌓는 구조를 사용합니다.&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;이렇게 쌓은 D램 칩들은 내부 연결 기술을 통해 짧은 거리에서 데이터를 주고받습니다. 데이터가 이동하는 거리가 짧고, 통로가 넓기 때문에 많은 데이터를 빠르게 전달할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HBM을 이해할 때 핵심은 다음 3가지입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 여러 개의 D램을 위로 쌓는다. 2. 칩 사이를 촘촘하게 연결한다. 3. GPU나 AI 가속기 가까이에 배치해 데이터를 빠르게 주고받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조 덕분에 HBM은 일반 D램보다 높은 대역폭을 제공할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;일반 D램과 HBM의 차이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 D램과 HBM은 모두 데이터를 임시로 저장하는 메모리입니다. 하지만 구조와 사용 목적이 다릅니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Gemini_Generated_Image_lzrt3jlzrt3jlzrt.png&quot; data-origin-width=&quot;2816&quot; data-origin-height=&quot;1536&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/K9n4i/dJMcaf0X5Ip/P4GVS55a8LP7Gn5hXa5IYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/K9n4i/dJMcaf0X5Ip/P4GVS55a8LP7Gn5hXa5IYk/img.png&quot; data-alt=&quot;일반 D램 / HBM 구조 비교&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/K9n4i/dJMcaf0X5Ip/P4GVS55a8LP7Gn5hXa5IYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FK9n4i%2FdJMcaf0X5Ip%2FP4GVS55a8LP7Gn5hXa5IYk%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;2816&quot; height=&quot;1536&quot; data-filename=&quot;Gemini_Generated_Image_lzrt3jlzrt3jlzrt.png&quot; data-origin-width=&quot;2816&quot; data-origin-height=&quot;1536&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;일반 D램 / HBM 구조 비교&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;일반 D램&lt;/th&gt;
&lt;th&gt;HBM&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;구조&lt;/td&gt;
&lt;td&gt;칩을 주로 평면적으로 배치&lt;/td&gt;
&lt;td&gt;여러 D램 칩을 수직으로 적층&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;강점&lt;/td&gt;
&lt;td&gt;가격 경쟁력, 범용성, 대용량 구성&lt;/td&gt;
&lt;td&gt;높은 대역폭, 전력 효율, 공간 효율&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;주 사용처&lt;/td&gt;
&lt;td&gt;PC, 노트북, 일반 서버 메모리&lt;/td&gt;
&lt;td&gt;GPU, AI 가속기, 슈퍼컴퓨터&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;핵심 특징&lt;/td&gt;
&lt;td&gt;표준화된 범용 메모리&lt;/td&gt;
&lt;td&gt;고성능 반도체 패키지에 함께 탑재되는 특수 메모리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;단점&lt;/td&gt;
&lt;td&gt;물리적인 대역폭 한계&lt;/td&gt;
&lt;td&gt;높은 제조 난도와 비싼 가격&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보자 관점에서는 이렇게 이해하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;일반 D램은 넓게 쓰이는 범용 메모리이고, HBM은 AI&amp;middot;GPU 같은 고성능 연산을 위해 데이터 통로를 넓힌 특수 메모리입니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 HBM이 일반 D램의 완전한 대체재라는 뜻은 아닙니다. HBM은 대역폭이 중요한 고성능 환경에 적합하고, 일반 D램은 가격과 범용성이 중요한 환경에 적합합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;HBM이 AI 반도체에서 중요한 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 모델은 엄청난 양의 데이터를 반복해서 읽고 계산합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 생성형 AI가 문장을 만들거나 이미지를 생성할 때, 내부적으로는 수많은 숫자 계산이 계속 일어납니다. 이때 GPU나 AI 가속기는 계산을 담당하고, 메모리는 계산에 필요한 데이터를 계속 공급합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 연산 장치가 빨라질수록 메모리도 빨라져야 한다는 점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연산 장치가 데이터를 기다리느라 멈추는 시간이 길어지면 전체 성능이 떨어집니다. 이것을 쉽게 표현하면 &lt;b&gt;데이터 병목&lt;/b&gt;이라고 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HBM은 이 병목을 줄이는 데 중요한 역할을 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AI 모델이 커질수록 필요한 데이터 양이 늘어납니다.&lt;/li&gt;
&lt;li&gt;GPU는 더 많은 데이터를 빠르게 받아야 합니다.&lt;/li&gt;
&lt;li&gt;HBM은 넓은 대역폭으로 데이터를 공급합니다.&lt;/li&gt;
&lt;li&gt;결과적으로 AI 연산 성능을 끌어올리는 데 도움을 줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 HBM은 AI 반도체, GPU, 데이터센터 관련 뉴스에서 자주 함께 언급됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;HBM의 장점&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 데이터 전송 속도가 빠르다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HBM의 가장 큰 장점은 &lt;b&gt;높은 대역폭&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 번에 많은 데이터를 이동시킬 수 있기 때문에 GPU나 AI 가속기가 데이터를 기다리는 시간을 줄일 수 있습니다. 특히 AI 학습, AI 추론, 고성능 컴퓨팅처럼 데이터 이동량이 많은 작업에서 유리합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 전력 효율이 좋다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HBM은 칩 사이의 거리가 짧고, 넓은 통로로 데이터를 전달합니다. 이 구조는 같은 양의 데이터를 처리할 때 &lt;b&gt;전력 효율&lt;/b&gt;을 높이는 데 도움이 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 데이터센터에서는 성능만큼 전력 소비도 중요합니다. 서버가 많아질수록 전기요금과 냉각 비용이 커지기 때문입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 공간을 적게 차지한다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HBM은 D램을 수직으로 쌓는 구조입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 면적에서 더 많은 메모리 용량과 높은 대역폭을 구현할 수 있습니다. 공간이 제한된 GPU 패키지 안에서 고성능 메모리를 넣어야 할 때 유리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;HBM의 단점과 한계&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HBM이 항상 좋은 선택인 것은 아닙니다. 장점이 뚜렷한 만큼 단점도 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 제조 난도가 높다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HBM은 여러 개의 D램을 수직으로 쌓고 정밀하게 연결해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 메모리 칩을 많이 붙이는 문제가 아닙니다. &lt;b&gt;적층, 연결, 패키징, 발열 관리&lt;/b&gt;까지 모두 높은 기술 수준이 필요합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 가격이 비싸다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제조 공정이 복잡하고 &lt;b&gt;수율 관리가 어렵기 때문에&lt;/b&gt; 일반 D램보다 가격이 비쌉니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 HBM은 일반 소비자용 PC보다는 AI 서버, 데이터센터, 고성능 GPU 같은 &lt;b&gt;기업형&amp;middot;고성능 분야&lt;/b&gt;에 주로 사용됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 발열 관리가 어렵다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;D램을 위로 쌓으면 공간 효율은 좋아지지만, 열을 관리하기는 더 어려워질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 층수가 높아질수록 &lt;b&gt;내부에서 발생한 열을 밖으로 빼내는 설계&lt;/b&gt;가 중요해집니다. HBM이 발전할수록 성능뿐 아니라 냉각과 안정성도 함께 중요해지는 이유입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 공급을 빠르게 늘리기 어렵다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HBM은 일반 D램보다 생산 과정이 복잡합니다. 고객사 요구에 맞춘 검증과 패키징 과정도 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 수요가 갑자기 늘어나도 공급량을 단기간에 크게 늘리기 어렵습니다. HBM 관련 뉴스에서 &lt;b&gt;수율, 양산, 고객사 인증, 공급 계약&lt;/b&gt; 같은 표현이 자주 나오는 이유입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;HBM 세대는 어떻게 발전했나&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HBM은 한 번에 완성된 기술이 아닙니다. 세대를 거치면서 속도, 용량, 효율이 계속 개선되어 왔습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 흐름은 다음과 같습니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;세대&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HBM&lt;/td&gt;
&lt;td&gt;1세대 고대역폭 메모리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HBM2&lt;/td&gt;
&lt;td&gt;대역폭과 용량 개선&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HBM2E&lt;/td&gt;
&lt;td&gt;HBM2의 확장형 성격&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HBM3&lt;/td&gt;
&lt;td&gt;AI&amp;middot;고성능 컴퓨팅 수요에 맞춰 성능 향상&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HBM3E&lt;/td&gt;
&lt;td&gt;HBM3의 확장형으로, 2026년 기준 AI 반도체 분야에서 많이 언급&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HBM4&lt;/td&gt;
&lt;td&gt;차세대 고성능 AI&amp;middot;HPC 환경을 겨냥한 세대&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;ldquo;최신이면 항상 좋다&amp;rdquo;고 판단하면 안 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 성능은 용량, 대역폭, 전력 효율, 발열, 고객사 요구사항, 패키징 방식에 따라 달라집니다. 또한 어떤 GPU나 AI 가속기에 탑재되는지도 중요합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;HBM3E와 HBM4는 무엇인가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HBM3E는 HBM3의 확장형 세대로 볼 수 있습니다. AI 서버와 고성능 GPU 수요가 커지면서 더 높은 대역폭과 용량을 제공하기 위해 등장했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2026년 기준으로 HBM3E는 AI 반도체 시장에서 자주 언급되는 핵심 메모리입니다. 삼성전자, SK하이닉스, 마이크론 같은 메모리 업체들이 관련 제품과 기술을 경쟁적으로 개발하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HBM4는 그다음 세대입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HBM4는 더 높은 대역폭과 효율, 더 큰 용량을 목표로 합니다. AI 모델이 계속 커지고 데이터센터의 연산 수요가 늘어나면서 HBM4 같은 차세대 메모리의 중요성도 커지고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 일반 독자 입장에서는 세부 규격까지 모두 외울 필요는 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 이것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;HBM3E와 HBM4는 AI 반도체가 더 많은 데이터를 더 빠르게 처리하기 위해 발전 중인 고성능 메모리 세대입니다.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;HBM은 GPU인가, RAM인가, SSD인가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HBM을 처음 접하면 다른 부품과 헷갈리기 쉽습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HBM은 GPU가 아니다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GPU는 연산을 담당하는 반도체입니다. HBM은 GPU 옆에서 데이터를 공급하는 메모리입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, GPU가 계산하는 엔진이라면 HBM은 그 엔진에 연료를 빠르게 공급하는 장치에 가깝습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HBM은 일반 RAM과 같은 역할을 하지만 구조가 다르다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HBM도 넓은 의미에서는 D램 계열 메모리입니다. 데이터를 임시로 저장하고 빠르게 읽고 쓰는 역할을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 PC에 꽂는 일반 RAM과는 구조, 가격, 사용처가 다릅니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HBM은 SSD가 아니다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSD는 데이터를 장기간 저장하는 저장장치입니다. 컴퓨터를 꺼도 파일이 남아 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 HBM은 연산 중 필요한 데이터를 빠르게 주고받기 위한 메모리입니다. 전원이 꺼지면 데이터가 유지되는 저장장치가 아닙니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;HBM이 모든 컴퓨터에 들어가지 않는 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HBM이 빠르다면 모든 컴퓨터에 넣으면 좋을 것 같지만, 현실은 그렇지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 큰 이유는 비용과 필요성입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 문서 작업, 웹서핑, 영상 시청, 가벼운 게임에서는 HBM의 높은 대역폭이 꼭 필요하지 않습니다. 일반 D램만으로도 충분한 경우가 많습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HBM은 다음과 같은 환경에서 가치가 커집니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대규모 AI 모델 학습&lt;/li&gt;
&lt;li&gt;생성형 AI 추론 서버&lt;/li&gt;
&lt;li&gt;고성능 GPU&lt;/li&gt;
&lt;li&gt;슈퍼컴퓨터&lt;/li&gt;
&lt;li&gt;고성능 데이터센터&lt;/li&gt;
&lt;li&gt;전문 그래픽&amp;middot;연산 장비&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, HBM은 모든 기기에 필요한 메모리라기보다 &lt;b&gt;데이터를 매우 빠르게 처리해야 하는 고성능 영역에 적합한 메모리&lt;/b&gt;입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;HBM 관련 뉴스를 읽을 때 봐야 할 포인트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HBM 관련 뉴스를 볼 때는 단순히 &amp;ldquo;HBM을 만든다&amp;rdquo;는 표현만 보면 부족합니다. 아래 요소를 함께 보면 기사 내용을 더 정확하게 이해할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;몇 세대 HBM인가?&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HBM3인지, HBM3E인지, HBM4인지에 따라 의미가 다릅니다. 세대가 높아질수록 일반적으로 더 높은 성능을 목표로 하지만, 실제 경쟁력은 세부 사양을 함께 봐야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;몇 단으로 쌓았는가?&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HBM은 여러 개의 D램을 쌓는 구조입니다. 8단, 12단, 16단처럼 적층 수가 언급되는 경우가 많습니다. &lt;b&gt;적층 수가 늘어나면 용량을 키우는 데 유리하지만&lt;/b&gt;, 제조 난도와 발열 관리도 함께 중요해집니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;어떤 칩과 결합하는가?&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HBM은 혼자 쓰이는 부품이 아닙니다. GPU나 AI 가속기와 함께 사용됩니다. 따라서 &lt;b&gt;어떤 GPU 또는 AI 가속기에 탑재되는지&lt;/b&gt;, 어떤 고객사와 연결되는지가 중요합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;개발인지, 샘플 공급인지, 양산인지 구분했는가?&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반도체 뉴스에서 개발 성공, 샘플 공급, 양산, 공급 계약은 각각 의미가 다릅니다. 개발에 성공했다는 말이 곧바로 &lt;b&gt;대량 양산과 매출 발생&lt;/b&gt;을 뜻하지는 않습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;수율과 패키징 이슈가 언급되는가?&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HBM은 성능뿐 아니라 안정적인 생산도 중요합니다. 따라서 수율, 고객사 인증, 패키징 기술, 발열 관리 같은 표현이 함께 등장하면 눈여겨볼 필요가 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;초보자를 위한 한 문장 정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HBM은 여러 개의 D램을 수직으로 쌓아 데이터가 지나가는 통로를 넓힌 고성능 메모리입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 반도체에서 HBM이 중요한 이유는 GPU와 AI 가속기가 더 많은 데이터를 더 빠르게 받아야 하기 때문입니다. 연산 장치가 빨라질수록 메모리의 역할도 커지고, HBM은 그 병목을 줄이는 핵심 부품으로 주목받고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 HBM은 제조가 어렵고 가격이 비싸며, 모든 컴퓨터에 필요한 메모리는 아닙니다. 일반 PC보다 AI 서버, 데이터센터, 고성능 GPU처럼 대규모 데이터를 빠르게 처리해야 하는 분야에서 가치가 큽니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HBM을 이해하면 AI 반도체 뉴스를 읽을 때 왜 메모리 기업들이 함께 주목받는지 훨씬 쉽게 파악할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;FAQ&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HBM은 일반 RAM보다 무조건 좋은가요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;용도에 따라 다릅니다. HBM은 높은 대역폭이 필요한 AI 연산, GPU, 고성능 컴퓨팅에 유리합니다. 하지만 일반 PC 작업에서는 가격과 구조상 일반 D램이 더 적합한 경우가 많습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HBM은 SSD와 같은 저장장치인가요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아닙니다. SSD는 데이터를 장기간 저장하는 장치이고, HBM은 연산 중 데이터를 빠르게 주고받는 메모리입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HBM이 AI 반도체에서 중요한 이유는 무엇인가요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 연산은 많은 데이터를 계속 읽고 계산해야 합니다. HBM은 GPU나 AI 가속기에 데이터를 빠르게 공급해 병목을 줄이는 역할을 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HBM3E와 HBM4는 무엇이 다른가요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HBM3E는 HBM3의 확장형 세대이고, HBM4는 그다음 세대입니다. HBM4는 더 높은 대역폭과 효율, 용량을 목표로 하는 차세대 규격입니다. 다만 실제 제품 성능은 제조사, 적층 수, 패키징, 탑재되는 시스템에 따라 달라질 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 자료&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Samsung Semiconductor, HBM3E 제품 소개: https://semiconductor.samsung.com/dram/hbm/hbm3e/&lt;/li&gt;
&lt;li&gt;Samsung Newsroom, HBM3E 12H DRAM 발표 자료: https://news.samsung.com/global/samsung-develops-industry-first-36gb-hbm3e-12h-dram&lt;/li&gt;
&lt;li&gt;SK hynix Newsroom, HBM 기술 및 AI 메모리 관련 자료: https://news.skhynix.co.kr/&lt;/li&gt;
&lt;li&gt;JEDEC HBM3 표준 발표 자료: https://www.businesswire.com/news/home/20220127005320/en/JEDEC-Publishes-HBM3-Update-to-High-Bandwidth-Memory-HBM-Standard&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>AI</category>
      <category>AI반도체</category>
      <category>GPU</category>
      <category>HBM</category>
      <category>고대역폭메모리</category>
      <category>메모리반도체</category>
      <category>반도체기초</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/37</guid>
      <comments>https://notebase.tistory.com/entry/what-is-hbm-memory#entry37comment</comments>
      <pubDate>Fri, 22 May 2026 13:46:11 +0900</pubDate>
    </item>
    <item>
      <title>AI 반도체가 주목받는 이유: GPU와 HBM이 중요한 진짜 이유</title>
      <link>https://notebase.tistory.com/entry/why-ai-semiconductors-matter</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;AI 반도체가 주목받는 이유를 GPU, HBM, 데이터센터, 생성형 AI 수요 관점에서 쉽게 정리했습니다. 기존 반도체와 다른 점, 산업 변화, 주의할 점까지 함께 설명합니다.&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;AI 반도체가 주목받는 이유는 단순히 인공지능이 유행하기 때문만은 아닙니다. 생성형 AI, 데이터센터, 클라우드 서비스가 커지면서 기존 반도체보다 훨씬 많은 연산과 메모리 성능이 필요해졌기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전에는 반도체 시장을 이야기할 때 스마트폰, PC, 자동차가 중심이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금은 여기에 AI 데이터센터가 강하게 들어왔습니다.&lt;br /&gt;ChatGPT 같은 생성형 AI 서비스, 이미지 생성 AI, AI 검색, 코딩 도구, 기업용 AI 솔루션이 늘어나면서 서버 안에서 엄청난 계산을 처리할 반도체가 필요해졌습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 필요한 반도체가 바로 AI 반도체입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 AI 반도체는 GPU 하나만을 뜻하지 않습니다. GPU, NPU, HBM, AI 가속기, 첨단 패키징까지 포함한 &lt;b&gt;AI 연산 인프라 전체&lt;/b&gt;로 보는 것이 더 정확합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AI 반도체란 무엇인가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 반도체는 인공지능 연산을 빠르고 효율적으로 처리하기 위해 설계된 반도체를 말합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 CPU도 AI 연산을 할 수는 있습니다.&lt;br /&gt;하지만 대규모 AI 모델을 학습시키거나, 많은 사용자의 요청을 동시에 처리하기에는 효율이 떨어집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI는 같은 계산을 대량으로 반복하는 작업이 많습니다.&lt;br /&gt;그래서 병렬 연산에 강한 반도체가 유리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 AI 반도체는 다음과 같습니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;th&gt;주로 쓰이는 곳&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GPU&lt;/td&gt;
&lt;td&gt;대규모 병렬 연산에 강한 칩&lt;/td&gt;
&lt;td&gt;AI 학습, 데이터센터&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NPU&lt;/td&gt;
&lt;td&gt;AI 연산에 특화된 칩&lt;/td&gt;
&lt;td&gt;스마트폰, 노트북, 엣지 기기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TPU&amp;middot;ASIC&lt;/td&gt;
&lt;td&gt;특정 AI 작업에 맞게 설계된 칩&lt;/td&gt;
&lt;td&gt;클라우드, 대형 플랫폼&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HBM&lt;/td&gt;
&lt;td&gt;AI 칩에 데이터를 빠르게 공급하는 고대역폭 메모리&lt;/td&gt;
&lt;td&gt;AI 서버, 고성능 GPU&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;여기서 중요한 점은 AI 반도체가 단일 부품만을 의미하지 않는다는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GPU만 좋아도 충분하지 않습니다.&lt;br /&gt;메모리, 패키징, 네트워크, 전력 관리, 냉각 기술까지 함께 맞물려야 AI 서버가 제대로 작동합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AI 반도체가 갑자기 중요해진 이유&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 생성형 AI는 연산량이 매우 크다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성형 AI는 질문에 답하거나 이미지를 만들 때마다 많은 계산을 수행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 대규모 언어 모델은 수많은 &lt;b&gt;파라미터, 즉 AI가 학습한 판단 기준&lt;/b&gt;을 바탕으로 다음 단어를 예측합니다.&lt;br /&gt;사용자가 많아질수록 서버는 더 많은 요청을 처리해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 서비스가 커질수록 필요한 것은 두 가지입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫째, AI 모델을 학습시키는 연산 성능입니다.&lt;br /&gt;둘째, 실제 사용자의 요청을 빠르게 처리하는 &lt;b&gt;추론 성능&lt;/b&gt;입니다. 추론은 이미 학습된 AI 모델을 실제 서비스에서 사용하는 단계를 말합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초기에는 학습용 GPU 수요가 크게 주목받았습니다.&lt;br /&gt;하지만 AI 서비스가 실제 제품에 들어가면서 추론용 반도체 수요도 점점 커지고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, AI 반도체 수요는 &amp;ldquo;AI를 개발할 때&amp;rdquo;만 생기는 것이 아닙니다.&lt;br /&gt;&lt;b&gt;AI를 서비스하고 운영하는 과정에서도 계속 발생합니다.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 데이터센터 투자가 빠르게 늘고 있다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 반도체 수요의 중심에는 데이터센터가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 모델은 개인 PC 한 대에서 처리하기 어렵습니다.&lt;br /&gt;대부분의 대형 AI 서비스는 클라우드 데이터센터에서 작동합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터센터에는 GPU, HBM, 네트워크 장비, 저장장치, 전력 설비, 냉각 시스템이 함께 들어갑니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 엔비디아의 실적을 보면 AI 데이터센터 수요가 얼마나 큰지 확인할 수 있습니다. NVIDIA는 &lt;b&gt;2026년 1월 25일 종료된 2026 회계연도 4분기 실적&lt;/b&gt;에서 데이터센터 매출이 &lt;b&gt;623억 달러&lt;/b&gt;, 전년 대비 &lt;b&gt;75% 증가&lt;/b&gt;했다고 발표했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 숫자는 AI 반도체가 단순한 기술 트렌드가 아니라 실제 매출로 연결되고 있다는 점을 보여줍니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. GPU만큼 HBM도 중요해졌다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 반도체를 이야기할 때 GPU만 주목하기 쉽습니다.&lt;br /&gt;하지만 실제로는 HBM도 핵심 부품입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HBM은 High Bandwidth Memory의 약자입니다.&lt;br /&gt;말 그대로 데이터를 매우 빠르게 주고받을 수 있는 고대역폭 메모리입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 연산에서는 칩이 아무리 빨라도 데이터를 제때 공급받지 못하면 성능이 떨어집니다.&lt;br /&gt;GPU가 &lt;b&gt;계산&lt;/b&gt;을 담당한다면, HBM은 계산할 데이터를 &lt;b&gt;빠르게 전달하는 역할&lt;/b&gt;을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 모델이 커질수록 더 많은 데이터를 동시에 처리해야 합니다.&lt;br /&gt;그래서 AI 서버에는 일반 메모리보다 훨씬 비싼 HBM이 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때문에 SK하이닉스, 삼성전자, 마이크론 같은 메모리 기업도 AI 반도체 흐름에서 중요한 위치를 차지하게 됐습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 기존 반도체 시장의 중심이 바뀌고 있다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거 반도체 수요는 스마트폰, PC, TV, 가전, 자동차가 중심이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 지금도 이 시장은 중요합니다.&lt;br /&gt;하지만 성장의 중심은 AI 인프라 쪽으로 이동하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gartner는 2026년 전 세계 반도체 매출이 1조 3천억 달러를 넘을 것으로 전망했습니다. 또한 AI 반도체가 전체 반도체 매출의 약 30%를 차지할 것으로 봤습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 AI 반도체가 일부 고성능 서버에만 쓰이는 특수 부품이 아니라, 반도체 산업 전체의 성장 방향을 바꾸는 핵심 분야가 됐다는 의미입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기존 반도체와 AI 반도체는 무엇이 다를까&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 반도체는 범용성이 중요했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CPU는 운영체제 실행, 문서 작업, 웹 브라우징, 앱 실행처럼 다양한 작업을 처리합니다.&lt;br /&gt;하지만 AI 연산은 성격이 다릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI는 대규모 행렬 계산, 반복 연산, 병렬 처리 비중이 큽니다.&lt;br /&gt;그래서 범용 CPU보다 GPU나 AI 전용 칩이 더 효율적입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 비유하면 이렇습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CPU는 여러 일을 골고루 처리하는 사무직에 가깝습니다.&lt;br /&gt;GPU는 같은 유형의 계산을 수천 개씩 동시에 처리하는 대규모 작업반에 가깝습니다.&lt;br /&gt;NPU나 ASIC은 특정 AI 업무에 맞춰 설계된 전용 장비에 가깝습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 시대에는 &amp;ldquo;어떤 반도체가 가장 빠른가&amp;rdquo;보다 &lt;b&gt;&amp;ldquo;어떤 작업을 가장 효율적으로 처리하는가&amp;rdquo;&lt;/b&gt;가 더 중요해지고 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AI 반도체가 중요한 산업들&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 반도체는 데이터센터에만 쓰이지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로는 여러 산업으로 확산될 가능성이 큽니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;클라우드와 검색 서비스&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 검색, 챗봇, 문서 요약, 코드 생성 서비스는 대부분 클라우드에서 작동합니다.&lt;br /&gt;사용자가 질문할 때마다 서버에서는 AI 연산이 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 분야에서는 GPU, AI 가속기, HBM, 네트워크 반도체가 중요합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;스마트폰과 PC&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스마트폰과 노트북에도 AI 기능이 들어가고 있습니다.&lt;br /&gt;사진 보정, 음성 인식, 실시간 번역, 문서 요약 같은 기능은 기기 안에서 직접 처리될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때는 서버용 GPU보다 저전력 NPU가 중요합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;자동차&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자율주행과 운전자 보조 시스템에도 AI 반도체가 필요합니다.&lt;br /&gt;차량은 카메라, 레이더, 라이다 등에서 들어오는 데이터를 실시간으로 처리해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;속도뿐 아니라 안정성, 전력 효율, 발열 관리도 중요합니다.&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;br /&gt;이 과정에서도 AI 연산이 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 반도체는 로봇이 클라우드에만 의존하지 않고 현장에서 빠르게 판단하도록 돕는 역할을 할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;왜 엔비디아가 자주 언급될까&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 반도체 뉴스에서 엔비디아가 자주 나오는 이유는 GPU 시장에서 강한 위치를 갖고 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엔비디아는 단순히 GPU만 파는 회사가 아닙니다.&lt;br /&gt;AI 개발에 필요한 소프트웨어 생태계, 개발 도구, 서버 시스템, 네트워크 기술까지 함께 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 기업 입장에서는 칩 성능만 보는 것이 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 환경이 안정적인지, 기존 AI 프레임워크와 잘 맞는지, 대규모 서버 운영이 쉬운지도 중요합니다.&lt;br /&gt;이 부분에서 엔비디아는 강한 생태계를 구축해 왔습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 AI 반도체 시장이 엔비디아만의 시장으로 고정된 것은 아닙니다.&lt;br /&gt;구글, 아마존, 마이크로소프트 같은 빅테크 기업은 자체 AI 칩을 개발하고 있습니다. AMD, 브로드컴, 인텔 등도 AI 반도체 시장을 공략하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로는 범용 GPU와 맞춤형 AI 칩이 함께 경쟁하는 구조가 될 가능성이 큽니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AI 반도체가 계속 성장하려면 필요한 조건&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 반도체 시장이 커지려면 단순히 AI에 대한 관심만으로는 부족합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 서비스에서 비용 대비 효과가 나와야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 모델을 운영하려면 막대한 서버 비용이 필요합니다.&lt;br /&gt;전기료, 냉각 비용, 장비 비용, 유지보수 비용도 함께 증가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기업이 AI 인프라에 계속 투자하려면 AI 서비스가 실제 매출 증가, 비용 절감, 업무 효율 개선으로 이어져야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 하나의 변수는 공급망입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 반도체는 설계만 잘한다고 바로 만들 수 없습니다.&lt;br /&gt;첨단 파운드리, HBM 공급, 첨단 패키징, 반도체 장비, 소재가 모두 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 공정이나 부품에서 병목이 생기면 전체 공급이 제한될 수 있습니다.&lt;br /&gt;최근 AI 수요가 커지면서 HBM, 첨단 패키징, 반도체 장비 쪽이 함께 주목받는 이유도 여기에 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AI 반도체를 볼 때 주의할 점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 반도체가 중요한 것은 맞지만, 모든 반도체 기업이 같은 방식으로 수혜를 받는 것은 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 반도체 시장은 크게 보면 성장하고 있지만, 기업별 위치는 다릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 기업은 GPU를 설계합니다.&lt;br /&gt;어떤 기업은 HBM을 만듭니다.&lt;br /&gt;어떤 기업은 파운드리에서 생산을 담당합니다.&lt;br /&gt;또 어떤 기업은 반도체 장비나 설계 소프트웨어를 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 AI 반도체를 이해할 때는 단순히 &amp;ldquo;AI 관련주&amp;rdquo;처럼 넓게 묶기보다, 어느 단계에서 어떤 역할을 하는지 보는 것이 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 AI 인프라 투자가 빠르게 늘어난 만큼, 향후에는 투자 대비 수익성에 대한 검증도 더 강해질 수 있습니다.&lt;br /&gt;AI 서비스가 실제로 돈을 벌 수 있는지, 데이터센터 비용을 감당할 수 있는지, 전력 공급은 충분한지 같은 질문이 중요해질 것입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정리: AI 반도체는 AI 시대의 기반 인프라다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 반도체가 주목받는 이유는 명확합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 모델은 더 커지고 있고, 사용자는 더 많아지고 있으며, 기업들은 AI를 실제 서비스와 업무에 적용하려고 합니다.&lt;br /&gt;이 흐름을 뒷받침하려면 막대한 연산 성능과 빠른 메모리, 안정적인 데이터센터 인프라가 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 GPU, HBM, NPU, AI 가속기 같은 반도체가 중요해졌습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 반도체는 단순히 반도체 업계의 유행어가 아닙니다.&lt;br /&gt;생성형 AI가 실제 서비스로 확산되는 과정에서 반드시 필요한 기반 기술입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 모든 기업이 같은 수혜를 받는 것은 아니므로, AI 반도체를 볼 때는 다음 세 가지를 함께 보는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 어떤 연산을 처리하는 반도체인가 2. 데이터센터, 기기, 자동차 중 어디에 쓰이는가 3. GPU, 메모리, 파운드리, 장비 중 어떤 위치에 있는가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 기준으로 보면 AI 반도체 뉴스가 훨씬 쉽게 읽힙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI</category>
      <category>AI반도체</category>
      <category>GPU</category>
      <category>HBM</category>
      <category>npu</category>
      <category>데이터센터</category>
      <category>반도체산업</category>
      <category>생성형AI</category>
      <category>엔비디아</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/36</guid>
      <comments>https://notebase.tistory.com/entry/why-ai-semiconductors-matter#entry36comment</comments>
      <pubDate>Fri, 22 May 2026 13:29:12 +0900</pubDate>
    </item>
    <item>
      <title>온디바이스 AI 뜻과 장단점, 클라우드 AI와 무엇이 다를까?</title>
      <link>https://notebase.tistory.com/entry/on-device-ai-meaning-pros-cons</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;온디바이스 AI의 뜻과 장단점을 쉽게 정리했습니다. 클라우드 AI와의 차이, 개인정보 보호, 속도, 오프라인 사용 가능 여부, NPU와 AI PC 흐름까지 함께 설명합니다.&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;온디바이스 AI는 스마트폰, 노트북, 태블릿 같은 기기 안에서 직접 AI 연산을 처리하는 기술입니다. 인터넷 서버로 데이터를 보내지 않고 기기 자체에서 AI 기능을 실행한다는 점이 핵심입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 스마트폰과 AI PC에서 온디바이스 AI가 자주 언급되는 이유도 여기에 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 기능을 더 빠르게 쓰고, 개인정보를 덜 외부로 보내며, 일부 기능은 인터넷 없이도 사용할 수 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 온디바이스 AI가 클라우드 AI보다 항상 더 좋은 것은 아닙니다. 기기 성능, 배터리, 모델 크기, 기능 범위에 따라 장단점이 뚜렷하게 갈립니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;온디바이스 AI 뜻&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;온디바이스 AI는 말 그대로 &lt;b&gt;기기 위에서 실행되는 AI&lt;/b&gt;를 뜻합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 AI 서비스는 보통 사용자의 입력을 서버로 보낸 뒤, 서버에서 AI 모델이 답변을 만들고 다시 사용자에게 결과를 보내는 방식으로 작동합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 온디바이스 AI는 가능한 작업을 기기 내부에서 처리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 스마트폰에서 다음과 같은 작업을 한다고 가정해 보겠습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사진 속 인물이나 사물 인식&lt;/li&gt;
&lt;li&gt;음성 명령 처리&lt;/li&gt;
&lt;li&gt;실시간 번역&lt;/li&gt;
&lt;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;이 중 일부 작업은 서버 도움 없이 스마트폰 내부 칩셋과 AI 모델만으로 처리될 수 있습니다. 이런 방식이 온디바이스 AI입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 예시로는 삼성 Galaxy AI의 &lt;b&gt;Live Translate&lt;/b&gt;처럼 통화나 메시지 상황에서 번역을 도와주는 기능, Apple Intelligence처럼 기기 안의 개인 맥락을 활용해 알림이나 문장 작업을 처리하는 기능을 들 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 모든 AI 작업을 무조건 기기 안에서만 처리하는 것은 아닙니다. 작업 난이도와 기기 성능에 따라 온디바이스와 클라우드를 함께 쓰는 구조도 많습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;클라우드 AI와 온디바이스 AI의 차이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;온디바이스 AI를 이해하려면 클라우드 AI와 비교하면 쉽습니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;온디바이스 AI&lt;/th&gt;
&lt;th&gt;클라우드 AI&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;처리 위치&lt;/td&gt;
&lt;td&gt;스마트폰, PC 등 사용자 기기 내부&lt;/td&gt;
&lt;td&gt;외부 서버, 데이터센터&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;인터넷 연결&lt;/td&gt;
&lt;td&gt;일부 기능은 오프라인 가능&lt;/td&gt;
&lt;td&gt;대부분 인터넷 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;속도&lt;/td&gt;
&lt;td&gt;짧은 작업은 빠를 수 있음&lt;/td&gt;
&lt;td&gt;네트워크 상태에 영향&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;개인정보&lt;/td&gt;
&lt;td&gt;외부 전송을 줄일 수 있음&lt;/td&gt;
&lt;td&gt;서버 전송 과정이 필요할 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;성능 한계&lt;/td&gt;
&lt;td&gt;기기 성능에 제한&lt;/td&gt;
&lt;td&gt;대형 모델 사용 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;대표 예시&lt;/td&gt;
&lt;td&gt;사진 보정, 음성 인식, 간단한 요약&lt;/td&gt;
&lt;td&gt;고성능 챗봇, 대규모 문서 분석, 복잡한 생성 작업&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말하면 온디바이스 AI는 &lt;b&gt;내 기기 안에서 빠르게 반응하는 개인 비서&lt;/b&gt;에 가깝고, 클라우드 AI는 &lt;b&gt;더 큰 서버 자원을 활용하는 고성능 AI 서비스&lt;/b&gt;에 가깝습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 실제 서비스는 둘 중 하나만 쓰지 않는 경우가 많습니다. 간단한 작업은 기기에서 처리하고, 복잡한 작업은 클라우드로 보내는 하이브리드 방식이 늘고 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;온디바이스 AI가 주목받는 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;온디바이스 AI가 주목받는 가장 큰 이유는 AI 기능이 스마트폰과 PC의 기본 기능처럼 들어오기 시작했기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거에는 AI를 쓰려면 별도 앱이나 웹사이트에 접속하는 경우가 많았습니다. 하지만 이제는 운영체제, 카메라, 키보드, 메모 앱, 검색 기능 안에 AI가 들어가고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 모바일 칩셋과 노트북 프로세서에는 &lt;b&gt;NPU&lt;/b&gt;라는 AI 연산 전용 장치가 들어가는 경우도 많습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NPU는 Neural Processing Unit의 약자로, AI 계산을 효율적으로 처리하기 위한 칩입니다. 쉽게 말해 CPU가 모든 일을 혼자 처리하지 않도록, AI 연산만 따로 맡아주는 보조 엔진에 가깝습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 흐름은 스마트폰뿐 아니라 노트북 시장에서도 중요해지고 있습니다. Microsoft의 Copilot+ PC는 AI 작업을 위한 NPU를 핵심 요소로 설명하며, 40 TOPS 이상의 NPU 성능을 기준으로 제시합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 온디바이스 AI는 단순한 소프트웨어 기능이 아니라 스마트폰, 노트북, 반도체 시장과도 연결된 흐름입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;온디바이스 AI 장점&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 개인정보 보호에 유리하다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;온디바이스 AI의 가장 큰 장점은 개인정보 보호입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 기능을 사용할 때 입력한 문장, 음성, 사진, 위치 정보, 일정 정보가 모두 서버로 전송된다면 사용자는 불안할 수밖에 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;온디바이스 AI는 필요한 데이터를 기기 내부에서 처리할 수 있기 때문에 외부 서버로 보내는 정보를 줄일 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 정보는 민감할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개인 사진&lt;/li&gt;
&lt;li&gt;통화 내용&lt;/li&gt;
&lt;li&gt;음성 명령&lt;/li&gt;
&lt;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;이런 데이터를 기기 안에서 처리할 수 있다면 보안과 프라이버시 측면에서 장점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 &amp;ldquo;온디바이스 AI = 모든 데이터가 절대 외부로 나가지 않는다&amp;rdquo;는 뜻은 아닙니다. 기능에 따라 클라우드 처리가 함께 쓰일 수 있습니다. 그래서 실제 서비스별 개인정보 처리 방식을 확인하는 것이 중요합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 반응 속도가 빠를 수 있다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;온디바이스 AI는 서버에 요청을 보내고 다시 결과를 받을 필요가 줄어듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 간단한 작업은 더 빠르게 처리될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 카메라 앱에서 실시간으로 배경을 분리하거나, 음성 명령을 즉시 인식하거나, 키보드가 다음 단어를 추천하는 기능은 빠른 반응이 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 작업은 서버 왕복 시간이 줄어들수록 사용자 경험이 좋아집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 네트워크가 불안정한 환경에서는 차이가 더 커질 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 인터넷이 없어도 일부 기능을 쓸 수 있다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라우드 AI는 대부분 인터넷 연결이 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 온디바이스 AI는 기기 안에 AI 모델이 들어가 있기 때문에 일부 기능은 오프라인에서도 작동할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 상황에서 유용할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비행기 안&lt;/li&gt;
&lt;li&gt;지하철이나 터널&lt;/li&gt;
&lt;li&gt;해외여행 중 데이터 연결이 불안정한 상황&lt;/li&gt;
&lt;li&gt;보안상 외부망 연결이 제한된 업무 환경&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 모든 기능이 오프라인으로 작동하는 것은 아닙니다. 고성능 생성형 AI 기능이나 최신 정보 검색 기능은 여전히 인터넷 연결이 필요할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 개인화 기능에 유리하다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;온디바이스 AI는 사용자의 기기 안에 있는 정보를 바탕으로 개인화된 기능을 제공하기 쉽습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 스마트폰 안에는 사용자의 일정, 연락처, 사진, 앱 사용 패턴, 알림 기록 등이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정보를 외부 서버로 많이 보내지 않고 기기 안에서 분석할 수 있다면 더 안전하면서도 개인화된 기능을 만들 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 다음과 같은 기능입니다.&lt;/p&gt;
&lt;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;li&gt;사용자 습관 기반 앱 추천&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인화 기능은 사용자가 자주 쓰는 기기와 결합될수록 가치가 커집니다. 단순히 똑똑한 AI가 아니라, 내 사용 패턴과 상황을 반영하는 AI에 가까워지기 때문입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 서비스 제공 비용을 줄일 수 있다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 서비스를 클라우드에서만 운영하면 서버 비용이 많이 듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 많아질수록 데이터센터, GPU, 네트워크 비용이 커집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;온디바이스 AI는 일부 연산을 사용자 기기에서 처리하기 때문에 서비스 제공자 입장에서는 서버 부담을 줄일 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 입장에서도 장기적으로는 더 많은 AI 기능이 기본 기능으로 제공될 가능성이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 이 비용 절감이 항상 사용자 가격 인하로 이어진다고 단정할 수는 없습니다. 기기 가격, 칩셋 비용, 소프트웨어 구독 모델 등 여러 요소가 함께 작용하기 때문입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;온디바이스 AI 단점&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 고성능 AI 작업에는 한계가 있다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;온디바이스 AI는 기기 안에서 작동하기 때문에 성능 제한이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스마트폰이나 노트북은 데이터센터 서버보다 전력, 발열, 메모리, 연산 능력에서 한계가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 매우 큰 AI 모델을 돌리거나 복잡한 작업을 처리하는 데는 클라우드 AI가 더 유리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음 작업은 클라우드 AI가 더 적합한 경우가 많습니다.&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;li&gt;최신 웹 정보 기반 답변&lt;/li&gt;
&lt;li&gt;긴 대화 맥락 유지&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;온디바이스 AI는 빠르고 가벼운 작업에는 좋지만, 모든 AI 작업을 대체하기는 어렵습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 기기 성능에 따라 사용 경험이 달라진다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;온디바이스 AI는 사용자의 기기 성능에 영향을 많이 받습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최신 스마트폰이나 AI PC에서는 잘 작동하는 기능이 구형 기기에서는 느리거나 아예 지원되지 않을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 생성형 AI 기능은 메모리와 AI 연산 성능을 많이 요구합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 같은 AI 기능이라도 다음 요소에 따라 차이가 날 수 있습니다.&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;NPU 성능&lt;/li&gt;
&lt;li&gt;메모리 용량&lt;/li&gt;
&lt;li&gt;운영체제 버전&lt;/li&gt;
&lt;li&gt;제조사의 AI 지원 정책&lt;/li&gt;
&lt;li&gt;배터리 상태&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때문에 온디바이스 AI 기능을 기대하고 기기를 구매한다면, 단순히 &amp;ldquo;AI 지원&amp;rdquo;이라는 문구만 볼 것이 아니라 어떤 기능이 실제로 기기 안에서 작동하는지 확인하는 것이 좋습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 배터리와 발열 문제가 생길 수 있다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 연산은 일반적인 앱 실행보다 더 많은 계산을 요구할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기기 안에서 AI를 계속 실행하면 배터리 소모가 늘어나거나 발열이 발생할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 다음 기능은 부담이 클 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실시간 음성 인식&lt;/li&gt;
&lt;li&gt;실시간 번역&lt;/li&gt;
&lt;li&gt;영상 분석&lt;/li&gt;
&lt;li&gt;이미지 생성&lt;/li&gt;
&lt;li&gt;긴 문서 요약&lt;/li&gt;
&lt;li&gt;로컬 LLM 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 최신 칩셋은 AI 연산을 효율적으로 처리하도록 설계되고 있습니다. 하지만 기기 내부에서 연산하는 이상 배터리와 발열 문제를 완전히 피하기는 어렵습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 모델 업데이트가 제한될 수 있다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라우드 AI는 서버의 모델만 업데이트하면 많은 사용자에게 빠르게 새 기능을 제공할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 온디바이스 AI는 기기 안에 모델이 들어가거나 운영체제 업데이트를 통해 기능이 제공되는 경우가 많습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 업데이트 속도가 느리거나, 제조사 지원이 끊긴 기기에서는 최신 AI 기능을 쓰기 어려울 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 기기 저장공간 문제도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 모델이 기기 내부에 저장되면 그만큼 저장공간을 차지할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 기능이 제한적으로 제공될 수 있다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;온디바이스 AI라는 표현이 붙어 있어도 모든 기능이 기기 내부에서 처리되는 것은 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로는 다음처럼 나뉠 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;완전 온디바이스 처리&lt;/li&gt;
&lt;li&gt;일부 온디바이스 처리&lt;/li&gt;
&lt;li&gt;클라우드 보조 처리&lt;/li&gt;
&lt;li&gt;대부분 클라우드 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 입장에서는 이 구분이 명확하게 보이지 않을 때가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 문장 요약은 기기 안에서 처리되지만, 복잡한 이미지 생성은 클라우드로 넘어갈 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 &amp;ldquo;이 기능이 온디바이스 AI인가?&amp;rdquo;를 볼 때는 단순한 마케팅 문구보다 실제 처리 방식과 개인정보 안내를 확인하는 것이 중요합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;온디바이스 AI가 유용한 경우&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;온디바이스 AI는 다음 상황에서 특히 유용합니다.&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;반복적으로 자주 쓰는 간단한 AI 기능&lt;/li&gt;
&lt;li&gt;스마트폰 기본 기능과 결합된 AI 기능&lt;/li&gt;
&lt;li&gt;사진, 음성, 키보드, 알림처럼 실시간 처리가 필요한 기능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 사진 앱에서 인물을 자동 분류하거나, 키보드가 문장을 추천하거나, 음성 명령을 빠르게 처리하는 기능은 온디바이스 AI와 잘 맞습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 복잡한 보고서 작성, 긴 문서 분석, 최신 정보 검색, 고품질 이미지 생성처럼 큰 연산이 필요한 작업은 클라우드 AI가 더 적합할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;온디바이스 AI와 클라우드 AI, 어느 쪽이 더 좋을까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘 중 하나가 무조건 더 좋다고 보기는 어렵습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;온디바이스 AI는 빠르고, 개인 정보 보호에 유리하며, 일부 기능을 오프라인으로 사용할 수 있다는 장점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라우드 AI는 더 큰 모델을 사용할 수 있고, 복잡한 작업을 처리하기 좋으며, 최신 기능을 빠르게 적용하기 쉽습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 앞으로는 둘 중 하나만 쓰는 방식보다 &lt;b&gt;하이브리드 AI&lt;/b&gt;가 더 일반적일 가능성이 큽니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하고 민감한 작업은 기기 안에서 처리하고, 복잡하고 큰 연산이 필요한 작업은 클라우드에서 처리하는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Apple Intelligence도 이런 흐름을 보여주는 사례입니다. 기본적으로 온디바이스 처리를 강조하면서도, 더 복잡한 요청에는 Private Cloud Compute를 함께 활용하는 구조를 제시하고 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;온디바이스 AI를 볼 때 확인할 점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 스마트폰이나 AI PC를 구매할 때는 다음을 확인하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 어떤 AI 기능이 실제로 온디바이스로 작동하는가? 2. 인터넷 없이 사용할 수 있는 기능은 무엇인가? 3. 구형 기기에서도 지원되는가? 4. 개인정보가 외부 서버로 전송되는 경우는 언제인가? 5. 배터리 소모나 발열은 어느 정도인가? 6. 운영체제 업데이트를 얼마나 오래 받을 수 있는가? 7. AI 기능이 무료인지, 구독이 필요한지 확인했는가? 8. AI PC라면 NPU 성능과 메모리 조건을 함께 확인했는가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 &amp;ldquo;AI 기능 지원&amp;rdquo;이라는 문구만으로는 충분하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 어떤 기능이 지원되는지, 어떤 조건에서 작동하는지, 어떤 데이터가 처리되는지까지 확인해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 Copilot+ PC처럼 AI PC를 표방하는 제품은 단순히 AI 기능이 있다는 점보다 NPU 성능, 메모리, 저장공간 같은 조건이 함께 중요합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;온디바이스 AI는 스마트폰, 노트북, 태블릿 같은 기기 안에서 직접 AI를 실행하는 기술입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 큰 장점은 개인정보 보호, 빠른 반응 속도, 오프라인 사용 가능성, 개인화입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 기기 성능에 영향을 받고, 고성능 AI 작업에는 한계가 있으며, 배터리와 발열 문제가 생길 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 AI 기능은 온디바이스와 클라우드 중 하나로만 나뉘기보다, 두 방식을 함께 쓰는 방향으로 발전할 가능성이 높습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 입장에서는 &amp;ldquo;온디바이스 AI가 들어갔다&amp;rdquo;는 표현만 보기보다, 실제로 어떤 기능이 기기 안에서 처리되는지 확인하는 것이 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI</category>
      <category>AI PC</category>
      <category>ai스마트폰</category>
      <category>appleintelligence</category>
      <category>galaxyai</category>
      <category>npu</category>
      <category>개인정보보호</category>
      <category>생성형AI</category>
      <category>온디바이스AI</category>
      <category>인공지능</category>
      <category>클라우드AI</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/35</guid>
      <comments>https://notebase.tistory.com/entry/on-device-ai-meaning-pros-cons#entry35comment</comments>
      <pubDate>Fri, 22 May 2026 13:25:34 +0900</pubDate>
    </item>
    <item>
      <title>파이썬으로 반복 작업 자동화하는 예시 4가지</title>
      <link>https://notebase.tistory.com/entry/python-task-automation-examples</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬으로 파일 이름 변경, 폴더 정리, 텍스트 검색, 엑셀 처리 같은 반복 작업을 자동화하는 초보자용 예제를 정리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 반복 작업 자동화는 사람이 매번 손으로 하던 파일 정리, 텍스트 검색, 엑셀 처리 같은 일을 코드로 처리하는 방법입니다. 복잡한 프로그램을 만들지 않아도, 작은 스크립트 하나만으로 반복 업무를 줄일 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬을 처음 배울 때는 변수, 조건문, 반복문 같은 문법이 따로 떨어져 보일 수 있습니다. 하지만 자동화 예제를 보면 이런 문법을 왜 배우는지 조금 더 쉽게 이해할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복 작업 자동화는 파이썬을 실생활이나 업무에 적용하기 좋은 첫 단계입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;파이썬 자동화란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 자동화는 &lt;b&gt;일정한 규칙이 있는 반복 작업&lt;/b&gt;을 코드로 처리하는 것을 말합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 이런 작업이 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파일 이름을 일정한 규칙으로 한 번에 바꾸기&lt;/li&gt;
&lt;li&gt;폴더 안의 파일을 확장자별로 분류하기&lt;/li&gt;
&lt;li&gt;여러 텍스트 파일에서 특정 단어 찾기&lt;/li&gt;
&lt;li&gt;엑셀 파일의 데이터를 읽고 조건에 맞게 정리하기&lt;/li&gt;
&lt;li&gt;반복적으로 생성해야 하는 문서나 로그 만들기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 단순합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사람이 반복해서 하는 일을 파이썬이 대신 반복하게 만드는 것&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬의 &lt;code&gt;for&lt;/code&gt;, &lt;code&gt;if&lt;/code&gt;, &lt;code&gt;pathlib&lt;/code&gt;, &lt;code&gt;shutil&lt;/code&gt;, &lt;code&gt;openpyxl&lt;/code&gt; 같은 기능을 조합하면 초보자도 간단한 자동화 코드를 만들 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;반복 작업 자동화가 필요한 상황&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 작업을 자동화하면 좋을지 모르겠다면, 아래 기준에 해당하는지 먼저 확인해보세요.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 같은 작업을 여러 번 반복한다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 100개의 이름을 하나씩 바꾸거나, 매번 같은 형식의 파일을 정리하는 작업이 여기에 해당합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 작업 규칙이 명확하다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &lt;code&gt;.jpg&lt;/code&gt; 파일은 이미지 폴더로, &lt;code&gt;.pdf&lt;/code&gt; 파일은 문서 폴더로 옮기는 식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;규칙이 명확할수록 코드로 자동화하기 쉽습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 단순하지만 실수하기 쉽다&lt;/b&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;이런 작업은 사람이 계속 처리하기보다 코드로 일정하게 처리하는 편이 더 안전할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;예시 1: 여러 파일 이름 한 번에 바꾸기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 흔한 자동화 예시는 파일 이름 변경입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 폴더 안에 있는 이미지 파일 이름을 아래처럼 바꾸고 싶다고 가정해보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;IMG_001.jpg -&amp;gt; photo_1.jpg
IMG_002.jpg -&amp;gt; photo_2.jpg
IMG_003.jpg -&amp;gt; photo_3.jpg&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;code&gt;pathlib&lt;/code&gt; 모듈을 사용하면 몇 줄로 처리할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;from pathlib import Path

# 작업할 폴더 지정
folder = Path(&quot;images&quot;)

# glob() 결과 순서는 고정되지 않을 수 있으므로 이름순으로 정렬합니다.
for index, file in enumerate(sorted(folder.glob(&quot;*.jpg&quot;)), start=1):
    # 새 파일 이름 규칙: photo_1.jpg, photo_2.jpg, ...
    new_name = folder / f&quot;photo_{index}.jpg&quot;

    # 같은 이름의 파일이 이미 있으면 덮어쓰기나 오류를 피하기 위해 건너뜁니다.
    if new_name.exists():
        print(f&quot;건너뜀: {new_name.name} 파일이 이미 있습니다.&quot;)
        continue

    file.rename(new_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;code&gt;images&lt;/code&gt; 폴더 안에 있는 &lt;code&gt;.jpg&lt;/code&gt; 파일을 찾아서 이름순으로 정렬한 뒤, 순서대로 새 이름을 붙입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 부분은 &lt;code&gt;sorted()&lt;/code&gt;, &lt;code&gt;for&lt;/code&gt; 반복문, &lt;code&gt;enumerate()&lt;/code&gt;입니다.&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;for index, file in enumerate(sorted(folder.glob(&quot;*.jpg&quot;)), start=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;code&gt;folder.glob(&quot;*.jpg&quot;)&lt;/code&gt;는 폴더 안의 jpg 파일을 찾고, &lt;code&gt;sorted()&lt;/code&gt;는 그 결과를 이름 기준으로 정렬합니다. &lt;code&gt;enumerate()&lt;/code&gt;는 파일마다 번호를 붙여줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일이 3개든 300개든 같은 방식으로 처리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의: 파일 이름 변경 코드는 되돌리기 번거로울 수 있습니다. 처음에는 반드시 복사본 폴더에서 테스트해보세요.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;예시 2: 폴더 안의 파일을 확장자별로 정리하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다운로드 폴더에는 여러 종류의 파일이 섞이기 쉽습니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;report.pdf
photo.jpg
memo.txt
data.xlsx&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 파일을 확장자별 폴더로 자동 정리할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;from pathlib import Path
import shutil

folder = Path(&quot;downloads&quot;)

for file in folder.iterdir():
    if file.is_file():
        # 확장자 추출: .pdf -&amp;gt; pdf
        extension = file.suffix.lower().replace(&quot;.&quot;, &quot;&quot;)

        if extension:
            # 확장자 이름의 폴더가 없으면 생성
            target_folder = folder / extension
            target_folder.mkdir(exist_ok=True)

            # 파일 이동
            shutil.move(str(file), str(target_folder / file.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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &lt;code&gt;report.pdf&lt;/code&gt;는 &lt;code&gt;pdf&lt;/code&gt; 폴더로, &lt;code&gt;photo.jpg&lt;/code&gt;는 &lt;code&gt;jpg&lt;/code&gt; 폴더로 이동합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 후 구조는 대략 이렇게 됩니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;downloads/
├── pdf/
│   └── report.pdf
├── jpg/
│   └── photo.jpg
├── txt/
│   └── memo.txt
└── xlsx/
    └── data.xlsx&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 자동화는 다운로드 폴더, 이미지 폴더, 문서 폴더를 정리할 때 유용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 실제 파일을 이동하는 코드이기 때문에 처음에는 테스트용 폴더에서 실행하는 것이 안전합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 대상 폴더에 이미 같은 이름의 파일이 있다면 환경에 따라 오류가 발생하거나 의도와 다르게 처리될 수 있습니다. 중요한 파일이 있는 폴더에서는 실행 전에 백업을 만들어두는 편이 좋습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;예시 3: 텍스트 파일에서 특정 단어 찾기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 텍스트 파일에서 특정 단어가 들어간 파일을 찾는 작업도 자동화할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &lt;code&gt;logs&lt;/code&gt; 폴더 안의 &lt;code&gt;.txt&lt;/code&gt; 파일에서 &lt;code&gt;error&lt;/code&gt;라는 단어를 찾는 코드입니다.&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;from pathlib import Path

folder = Path(&quot;logs&quot;)
keyword = &quot;error&quot;

for file in folder.glob(&quot;*.txt&quot;):
    content = file.read_text(encoding=&quot;utf-8&quot;)

    if keyword in content:
        print(f&quot;{file.name} 파일에 '{keyword}' 단어가 있습니다.&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;code&gt;logs&lt;/code&gt; 폴더 안의 텍스트 파일을 하나씩 읽고, 특정 단어가 포함되어 있는지 확인합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그 파일, 메모 파일, 문서 초안 등을 검색할 때 활용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 여러 로그 파일에서 에러 메시지를 찾아야 한다면 사람이 하나씩 열어보는 것보다 빠르게 확인할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;예시 4: 엑셀 파일 읽어서 데이터 필터링하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;업무 자동화에서 자주 나오는 것이 엑셀 처리입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서는 &lt;code&gt;openpyxl&lt;/code&gt; 라이브러리를 사용하면 엑셀 파일을 읽고 수정할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;openpyxl&lt;/code&gt;은 파이썬 표준 라이브러리가 아니라 외부 패키지입니다. 먼저 설치가 필요합니다.&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;pip install openpyxl&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;code&gt;sales.xlsx&lt;/code&gt; 파일에 아래와 같은 데이터가 있다고 가정해보겠습니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;상품명&lt;/th&gt;
&lt;th&gt;판매금액&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;키보드&lt;/td&gt;
&lt;td&gt;50000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;마우스&lt;/td&gt;
&lt;td&gt;30000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;모니터&lt;/td&gt;
&lt;td&gt;200000&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;이 데이터에서 판매금액이 100,000원 이상인 항목만 출력해보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;from openpyxl import load_workbook

# 엑셀 파일 불러오기
workbook = load_workbook(&quot;sales.xlsx&quot;)
sheet = workbook.active

# 두 번째 줄부터 데이터 읽기
# 첫 번째 줄은 제목 행이라고 가정
for row in sheet.iter_rows(min_row=2, values_only=True):
    name = row[0]
    amount = row[1]

    if amount &amp;gt;= 100000:
        print(f&quot;{name}: {amount}원&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;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;모니터: 200000원&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;이 코드는 엑셀 파일의 두 번째 줄부터 데이터를 읽고, 판매금액이 100,000원 이상인 항목만 출력합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엑셀 자동화는 조금만 익숙해져도 반복 업무에 바로 활용하기 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팁: 코드를 실행하기 전에 대상 엑셀 파일을 닫아두는 것이 좋습니다. 파일이 열려 있으면 환경에 따라 &lt;code&gt;PermissionError&lt;/code&gt;가 발생할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;자동화 코드를 작성할 때 주의할 점&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 자동화는 편리하지만, 파일을 수정하거나 이동하는 작업에서는 주의가 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드는 사람이 직접 클릭하는 것보다 훨씬 빠르게 여러 파일을 처리합니다. 그래서 작은 실수도 여러 파일에 한꺼번에 반영될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 내용은 자동화 코드를 실행하기 전에 꼭 확인하는 것이 좋습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 처음에는 테스트 폴더에서 실행하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 이름 변경, 이동, 삭제 코드는 실제 자료에 바로 실행하지 않는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 복사본이나 테스트 폴더를 만들어서 코드가 원하는 대로 동작하는지 확인해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;권장하지 않음: 원본 폴더에서 바로 실행
권장: 복사한 테스트 폴더에서 먼저 실행&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 습관만 있어도 실수로 중요한 파일을 잃을 가능성을 줄일 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 삭제보다 이동을 먼저 사용하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보자라면 &lt;code&gt;delete&lt;/code&gt;, &lt;code&gt;unlink&lt;/code&gt; 같은 삭제 기능보다 이동 기능을 먼저 사용하는 편이 안전합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일을 지우는 대신 &lt;code&gt;backup&lt;/code&gt; 폴더나 &lt;code&gt;trash&lt;/code&gt; 폴더로 옮기면 문제가 생겼을 때 되돌리기 쉽습니다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;import shutil
from pathlib import Path

file = Path(&quot;sample.txt&quot;)
backup_folder = Path(&quot;backup&quot;)

backup_folder.mkdir(exist_ok=True)

shutil.move(str(file), str(backup_folder / file.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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삭제 작업은 충분히 테스트한 뒤 사용하는 것이 좋습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. 경로를 정확히 확인하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동화 코드에서 자주 발생하는 실수 중 하나가 경로 문제입니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;from pathlib import Path

# 이 경로는 현재 작업 디렉터리(cwd)를 기준으로 해석됩니다.
folder = Path(&quot;downloads&quot;)
print(&quot;현재 작업 디렉터리 기준:&quot;, folder.resolve())

# 현재 파이썬 파일이 있는 위치를 기준으로 삼고 싶다면 이렇게 씁니다.
script_folder = Path(__file__).parent
download_folder = script_folder / &quot;downloads&quot;
print(&quot;스크립트 파일 기준:&quot;, download_folder.resolve())&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;code&gt;Path(&quot;downloads&quot;)&lt;/code&gt;는 현재 작업 디렉터리를 기준으로 찾습니다. 반대로 &lt;code&gt;Path(__file__).parent / &quot;downloads&quot;&lt;/code&gt;는 현재 파이썬 파일이 있는 폴더를 기준으로 경로를 만듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 자동화 작업 전에는 어떤 기준 경로를 쓰고 있는지 먼저 출력해보는 습관이 좋습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;처음에는 작은 작업부터 자동화하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 자동화를 처음 배울 때부터 큰 프로그램을 만들 필요는 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오히려 작은 작업부터 시작하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음 정도면 충분합니다.&lt;/p&gt;
&lt;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;li&gt;반복적으로 작성하는 문장 자동 생성하기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 것은 &amp;ldquo;이 작업을 사람이 반복하고 있는가?&amp;rdquo;를 생각해보는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복되는 규칙이 있다면 파이썬으로 자동화할 가능성이 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;마무리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬으로 반복 작업을 자동화하면 파일 정리, 텍스트 검색, 엑셀 처리 같은 단순 작업을 줄일 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일을 하나씩 정리하거나, 문서를 일일이 열어 확인하거나, 엑셀 데이터를 반복해서 검사하는 작업은 실수하기 쉽습니다. 이런 작업을 코드로 처리하면 더 빠르고 일정한 결과를 얻을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 파일 이름 바꾸기처럼 작은 예제부터 시작하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음 폴더 정리, 텍스트 검색, 엑셀 처리처럼 조금씩 범위를 넓혀가면 파이썬을 실제 작업에 활용하는 감각을 익힐 수 있습니다.&lt;/p&gt;</description>
      <category>개발/python</category>
      <category>업무자동화</category>
      <category>코딩입문</category>
      <category>파이썬</category>
      <category>파이썬예제</category>
      <category>파이썬자동화</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/34</guid>
      <comments>https://notebase.tistory.com/entry/python-task-automation-examples#entry34comment</comments>
      <pubDate>Fri, 22 May 2026 13:17:33 +0900</pubDate>
    </item>
    <item>
      <title>Gemini, ChatGPT, Claude 차이점 비교: 어떤 AI를 언제 쓰면 좋을까</title>
      <link>https://notebase.tistory.com/entry/gemini-chatgpt-claude-comparison</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Gemini, ChatGPT, Claude의 차이를 2026년 5월 기준 공식 정보와 실사용 관점으로 비교합니다. 글쓰기, 검색, 문서 정리, 코딩, Google 연동, 멀티모달 작업 기준으로 어떤 AI가 맞는지 정리했습니다.&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;code&gt;범용성은 ChatGPT&lt;/code&gt;, &lt;code&gt;Google 작업 흐름은 Gemini&lt;/code&gt;, &lt;code&gt;긴 문서 정리와 문장 다듬기는 Claude&lt;/code&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 이 세 AI는 단순 순위로 고르기보다, &lt;b&gt;내가 자주 하는 작업에 어디가 더 잘 맞는지&lt;/b&gt;로 비교하는 편이 훨씬 실용적입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 &lt;b&gt;2026년 5월 22일 기준&lt;/b&gt; OpenAI, Google, Anthropic의 공식 가격 페이지와 도움말을 바탕으로 정리했습니다. 다만 기능, 한도, 국가별 제공 범위는 자주 바뀔 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;세 AI를 단순 순위로 비교하면 안 되는 이유&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;셋 중에 뭐가 제일 좋아요?&amp;rdquo;라고 묻는 경우가 많지만, 실제로는 이 질문만으로 답하기 어렵습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이유는 간단합니다. AI는 &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;여러 작업을 한곳에서 처리하고 싶다면 ChatGPT가 편할 수 있습니다.&lt;/li&gt;
&lt;li&gt;Gmail, Docs, Drive 중심으로 일한다면 Gemini가 더 자연스럽게 붙습니다.&lt;/li&gt;
&lt;li&gt;긴 PDF, 보고서, 회의록처럼 긴 텍스트를 읽고 다듬는 작업은 Claude가 더 잘 맞을 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게다가 AI 서비스는 업데이트 속도가 매우 빠릅니다. 플랜 이름, 사용량 제한, 제공 기능, 국가별 지원 범위가 수시로 바뀝니다. 그래서 &amp;ldquo;무조건 A가 B보다 낫다&amp;rdquo;처럼 단정하면 글이 금방 낡습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 순위보다 아래 기준에 집중하겠습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;범용적으로 쓰기 좋은 AI는 무엇인가&lt;/li&gt;
&lt;li&gt;Google 서비스와 함께 쓰기 좋은 AI는 무엇인가&lt;/li&gt;
&lt;li&gt;긴 문서 정리에 강한 AI는 무엇인가&lt;/li&gt;
&lt;li&gt;자료 조사와 검색 흐름에 잘 맞는 AI는 무엇인가&lt;/li&gt;
&lt;li&gt;무료로 먼저 써보기 좋은 AI는 무엇인가&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;한눈에 비교하면&lt;/b&gt;&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;AI&lt;/th&gt;
&lt;th&gt;잘 맞는 상황&lt;/th&gt;
&lt;th&gt;강점&lt;/th&gt;
&lt;th&gt;먼저 확인할 점&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ChatGPT&lt;/td&gt;
&lt;td&gt;범용 작업, 초안 작성, 파일 분석, 코딩 보조&lt;/td&gt;
&lt;td&gt;검색, 파일 업로드, 음성, 이미지 도구를 한 서비스 안에서 폭넓게 쓰기 좋음&lt;/td&gt;
&lt;td&gt;플랜별 기능과 사용 한도가 자주 바뀜&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini&lt;/td&gt;
&lt;td&gt;Gmail, Docs, Drive, Android, 멀티모달 작업&lt;/td&gt;
&lt;td&gt;Google 서비스 연동, 이미지 생성&amp;middot;편집, 실시간 조사 계열 기능&lt;/td&gt;
&lt;td&gt;국가, 개인 계정/Workspace 계정, 플랜별 차이가 큼&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude&lt;/td&gt;
&lt;td&gt;긴 PDF, 보고서, 회의록, 장문 초안 정리&lt;/td&gt;
&lt;td&gt;긴 맥락 유지, 문장 정리, 장문 읽기와 구조화에 강점&lt;/td&gt;
&lt;td&gt;사용량은 대화 길이, 첨부파일 크기, 모델에 따라 달라짐&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 표만 먼저 기억해도 선택이 훨씬 쉬워집니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;ChatGPT는 어떤 용도에 적합할까&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChatGPT는 가장 무난한 출발점에 가깝습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenAI 공식 가격 페이지 기준으로 무료 플랜에서도 웹 검색, 파일 업로드, 음성, 이미지 도구를 제한적으로 써볼 수 있습니다. 유료 플랜에서는 사용 한도가 늘어나고 deep research 같은 고급 기능 접근이 넓어집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, ChatGPT의 핵심 장점은 &lt;b&gt;한 서비스 안에서 할 수 있는 일이 넓다&lt;/b&gt;는 점입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;ChatGPT가 잘 맞는 사람&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 작업을 한 AI 안에서 처리하고 싶은 사람&lt;/li&gt;
&lt;li&gt;글쓰기, 요약, 번역, 아이디어 정리를 자주 하는 사람&lt;/li&gt;
&lt;li&gt;파일을 올려 분석하거나 표 형태로 정리하고 싶은 사람&lt;/li&gt;
&lt;li&gt;코딩 질문, 오류 분석, 구조 정리를 함께 하고 싶은 사람&lt;/li&gt;
&lt;li&gt;처음 AI를 시작하면서 가장 무난한 선택지를 찾는 사람&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 ChatGPT는 &amp;ldquo;초안 만들기 -&amp;gt; 다시 써달라고 하기 -&amp;gt; 더 쉽게 설명해달라고 하기 -&amp;gt; 표로 정리해달라고 하기&amp;rdquo; 같은 대화형 작업 흐름에 강합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;ChatGPT 사용 시 주의할 점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;범용성이 높다고 해서 항상 가장 정확한 것은 아닙니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가격, 정책, 법률, 세금, 최신 제품 정보처럼 바뀌는 내용은 공식 페이지 재확인이 필요합니다.&lt;/li&gt;
&lt;li&gt;유료 플랜이라고 해도 모든 기능이 무제한은 아닙니다.&lt;/li&gt;
&lt;li&gt;플랜 구성은 공식 가격 페이지와 도움말 기준으로 세부 안내가 달라질 수 있으니 결제 전 실제 화면 확인이 안전합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 ChatGPT는 &lt;b&gt;하나만 먼저 써봐야 한다면 가장 추천하기 쉬운 AI&lt;/b&gt;입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Gemini는 어떤 용도에 적합할까&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gemini는 Google 생태계를 자주 쓰는 사람에게 강점이 더 분명합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Google 공식 도움말 기준으로 Gemini 앱의 유료 업그레이드는 현재 &lt;code&gt;Google AI Pro&lt;/code&gt;와 &lt;code&gt;Google AI Ultra&lt;/code&gt; 체계로 안내됩니다. Google One AI Premium이라는 옛 이름에 익숙한 사람도 있지만, 현재 공식 안내에서는 Pro와 Ultra 명칭을 쓰고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 Google은 Gemini를 단순 채팅 도구가 아니라, Gmail, Docs, Vids, Drive 같은 서비스와 연결되는 작업 도구로 계속 확장하고 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Gemini가 잘 맞는 사람&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Gmail, Docs, Drive, Sheets 등 Google 서비스를 많이 쓰는 사람&lt;/li&gt;
&lt;li&gt;검색 흐름과 AI 답변을 함께 활용하고 싶은 사람&lt;/li&gt;
&lt;li&gt;Android나 Google 계정 중심으로 작업하는 사람&lt;/li&gt;
&lt;li&gt;이미지, 영상, 음성 등 멀티모달 작업에도 관심이 있는 사람&lt;/li&gt;
&lt;li&gt;Google Workspace 흐름 안에서 AI를 쓰고 싶은 사람&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gemini의 핵심은 &lt;b&gt;Google 안에서 이어지는 작업 흐름&lt;/b&gt;입니다. 메일, 문서, 파일, 일정, 검색이 이미 Google 중심이라면 별도 도구를 붙이는 느낌보다 &amp;ldquo;기존 작업 환경에 AI가 들어온다&amp;rdquo;는 체감이 큽니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Gemini의 멀티모달 장점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Google 공식 도움말상 Gemini는 텍스트 답변뿐 아니라 이미지 생성과 편집 기능도 적극적으로 밀고 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Gemini에서 만든 이미지를 다시 수정할 수 있습니다.&lt;/li&gt;
&lt;li&gt;사용자가 올린 이미지를 편집하거나 여러 이미지를 합쳐 새 이미지를 만들 수 있습니다.&lt;/li&gt;
&lt;li&gt;Storybook 기능처럼 삽화가 있는 이야기 형태의 창작도 지원합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 Gemini는 단순 검색형 AI라기보다, &lt;b&gt;Google 연동 + 멀티모달 제작 도구&lt;/b&gt; 쪽으로 이해하는 편이 맞습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Gemini 사용 시 주의할 점&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일부 기능은 국가, 연령, 계정 유형, 플랜에 따라 제공 여부가 다릅니다.&lt;/li&gt;
&lt;li&gt;개인 Google 계정과 회사/학교 Workspace 계정은 접근 가능한 기능이 다를 수 있습니다.&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Google 서비스 연동이 강점&amp;rdquo;이라는 말은 반대로, Google을 거의 쓰지 않으면 장점 체감이 줄 수 있다는 뜻이기도 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 Gemini는 &lt;b&gt;Google 생태계에 깊게 들어가 있는 사용자에게 특히 잘 맞는 AI&lt;/b&gt;입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Claude는 어떤 용도에 적합할까&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude는 긴 문서를 읽고 정리하는 작업에서 강점을 느끼는 사용자가 많습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Anthropic 공식 도움말 기준으로 개인용 Claude는 Free, Pro, Max 체계로 안내됩니다. Pro는 유료 기본형이고, Max는 더 높은 사용량이 필요한 사용자를 위한 상위 단계입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude를 이해할 때 중요한 점은 &amp;ldquo;몇 번까지 쓸 수 있나&amp;rdquo;가 고정 숫자가 아니라는 점입니다. 공식 도움말도 사용량이 &lt;b&gt;메시지 길이, 첨부파일 크기, 현재 대화 길이, 사용하는 모델과 기능&lt;/b&gt;에 따라 달라진다고 설명합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Claude가 잘 맞는 사람&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;긴 PDF나 보고서를 자주 읽고 요약하는 사람&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;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Claude는 짧은 한두 줄 답보다, &lt;b&gt;장문을 구조화하고 흐름을 정리하는 작업&lt;/b&gt;에서 만족도가 높은 편입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래 같은 요청에 잘 맞습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 보고서의 핵심 주장만 뽑아줘&lt;/li&gt;
&lt;li&gt;회의록에서 결정 사항과 할 일을 분리해줘&lt;/li&gt;
&lt;li&gt;이 초안을 더 자연스럽고 덜 딱딱하게 다듬어줘&lt;/li&gt;
&lt;li&gt;긴 문서에서 빠진 논리나 리스크를 찾아줘&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Claude 사용 시 주의할 점&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용량 제한은 플랜이 같아도 대화 방식에 따라 크게 달라질 수 있습니다.&lt;/li&gt;
&lt;li&gt;긴 대화와 큰 파일 첨부는 사용량을 더 빨리 소모할 수 있습니다.&lt;/li&gt;
&lt;li&gt;최신 정보 검색이나 외부 서비스 연동은 사용 환경에 따라 체감 차이가 있을 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 Claude는 &lt;b&gt;긴 문서와 자연스러운 문장 정리에 강한 AI&lt;/b&gt;로 이해하면 가장 맞습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;용도별로 추천하면&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;누가 더 뛰어난가&amp;rdquo;보다 &amp;ldquo;무슨 작업을 하느냐&amp;rdquo;로 보면 훨씬 실용적입니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;용도&lt;/th&gt;
&lt;th&gt;먼저 추천할 AI&lt;/th&gt;
&lt;th&gt;이유&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;하나의 AI로 여러 작업 시작하기&lt;/td&gt;
&lt;td&gt;ChatGPT&lt;/td&gt;
&lt;td&gt;범용성이 높고 시작 장벽이 낮음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;블로그 글 초안 잡기&lt;/td&gt;
&lt;td&gt;ChatGPT&lt;/td&gt;
&lt;td&gt;구조 잡기, 요약, 재작성 요청이 편함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;긴 초안 문장 다듬기&lt;/td&gt;
&lt;td&gt;Claude&lt;/td&gt;
&lt;td&gt;장문 정리와 문장 흐름 보정에 유리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gmail, Docs, Drive와 함께 쓰기&lt;/td&gt;
&lt;td&gt;Gemini&lt;/td&gt;
&lt;td&gt;Google 작업 흐름 안에서 연결성이 좋음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;긴 PDF, 보고서, 회의록 정리&lt;/td&gt;
&lt;td&gt;Claude&lt;/td&gt;
&lt;td&gt;긴 문맥 유지와 구조화가 강점&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;이미지와 텍스트를 같이 다루기&lt;/td&gt;
&lt;td&gt;Gemini 또는 ChatGPT&lt;/td&gt;
&lt;td&gt;멀티모달 작업 접근성이 좋음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;코딩 질문과 범용 작업 병행&lt;/td&gt;
&lt;td&gt;ChatGPT&lt;/td&gt;
&lt;td&gt;코딩 보조와 일반 작업을 함께 쓰기 편함&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로는 하나만 고르기보다 역할을 나눠 쓰는 사람이 많습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일상 질문과 범용 작업은 ChatGPT&lt;/li&gt;
&lt;li&gt;Google 연동과 멀티모달 작업은 Gemini&lt;/li&gt;
&lt;li&gt;긴 문서 읽기와 문장 다듬기는 Claude&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 조합이 현실적으로 가장 설명이 잘 됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;무료 버전과 유료 버전은 어떻게 고르면 될까&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 결제할 필요는 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 서비스 모두 무료로 기본 작업을 시험해볼 수 있으니, 먼저 아래 질문으로 판단하는 편이 좋습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;매일 업무에 AI를 쓰는가&lt;/li&gt;
&lt;li&gt;긴 문서나 파일을 자주 올리는가&lt;/li&gt;
&lt;li&gt;사용량 제한에 자주 걸리는가&lt;/li&gt;
&lt;li&gt;deep research, 고급 모델, 이미지 편집 같은 기능이 꼭 필요한가&lt;/li&gt;
&lt;li&gt;내 작업 흐름에 Google 연동이나 장문 처리 같은 특정 강점이 중요한가&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래에 가깝다면 유료 플랜을 고려할 만합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AI를 거의 매일 쓴다&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;반대로 가끔 질문하거나 초안만 만드는 정도라면 무료 버전부터 시작해도 충분합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 &amp;ldquo;좋아 보이니까 결제&amp;rdquo;가 아니라, &lt;b&gt;내가 자주 막히는 작업을 해결해 주는가&lt;/b&gt;입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;결국 어떤 AI부터 써보면 좋을까&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나만 먼저 시작해야 한다면 ChatGPT가 가장 무난합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이미 업무 대부분이 Google 안에서 돌아간다면 Gemini를 먼저 써보는 편이 더 만족스러울 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;긴 문서, 보고서, 회의록, 제안서 초안 정리가 많다면 Claude를 함께 비교해보는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;짧게 정리하면 이렇게 추천할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;가장 무난한 첫 선택&lt;/b&gt;: ChatGPT&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Google 서비스 중심 업무&lt;/b&gt;: Gemini&lt;/li&gt;
&lt;li&gt;&lt;b&gt;긴 문서와 자연스러운 문장 정리&lt;/b&gt;: Claude&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;정리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gemini, ChatGPT, Claude는 모두 좋은 AI입니다. 다만 잘하는 일이 조금씩 다릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChatGPT는 범용성이 좋고, Gemini는 Google 생태계와 멀티모달 작업에서 장점이 크고, Claude는 긴 문서와 장문 정리에 잘 맞습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 것은 &amp;ldquo;가장 강한 AI&amp;rdquo;를 찾는 것이 아니라, &lt;b&gt;내가 자주 하는 작업에 가장 잘 맞는 AI를 고르는 것&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 무료 버전으로 직접 써보고, 이후에 자주 쓰는 작업이 분명해졌을 때 유료 플랜을 검토하는 편이 가장 안전합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;FAQ&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;셋 중 하나만 먼저 시작한다면 무엇이 좋을까&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 생태계 제약이 없다면 ChatGPT가 가장 무난합니다. 다만 Google 중심 업무라면 Gemini, 긴 문서 정리가 핵심이라면 Claude가 더 잘 맞을 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Gemini는 검색에 강하고 ChatGPT는 글쓰기에 강하다고 단정해도 될까&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 단정하기는 어렵습니다. 두 서비스 모두 계속 바뀌고 있고, 실제 체감은 사용하는 플랜과 작업 방식에 따라 달라집니다. 다만 Google 서비스 연동은 Gemini 쪽이, 범용 대화형 초안 작업은 ChatGPT 쪽이 상대적으로 이해하기 쉽습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Claude는 왜 긴 문서에 강하다고 느끼는 사람이 많을까&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장문 맥락을 유지하면서 구조를 정리하고 문장을 다듬는 용도로 많이 쓰이기 때문입니다. 특히 긴 PDF, 회의록, 기획서처럼 한 번에 읽고 정리해야 하는 자료에서 장점이 잘 드러납니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;무료 버전만으로도 충분할까&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가벼운 질문, 요약, 초안 작성 정도라면 충분할 수 있습니다. 다만 매일 업무에 쓰거나 긴 파일과 고급 기능이 필요하면 유료 플랜 쪽이 더 잘 맞을 가능성이 큽니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI</category>
      <category>ai비교</category>
      <category>AI챗봇</category>
      <category>AI추천</category>
      <category>chatGPT</category>
      <category>Claude</category>
      <category>Gemini</category>
      <category>생성형AI</category>
      <category>업무용ai</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/33</guid>
      <comments>https://notebase.tistory.com/entry/gemini-chatgpt-claude-comparison#entry33comment</comments>
      <pubDate>Fri, 22 May 2026 11:41:32 +0900</pubDate>
    </item>
    <item>
      <title>AI 에이전트란? 초보자가 이해하기 쉽게 정리</title>
      <link>https://notebase.tistory.com/entry/what-is-ai-agent-beginner-guide</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트란 무엇인지 초보자도 이해할 수 있게 정리했습니다. ChatGPT와의 차이, 작동 방식, 활용 예시, 주의할 점까지 쉽게 설명합니다.&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;AI 에이전트는 사용자의 목표를 이해하고, 필요한 단계를 계획한 뒤, 도구를 사용해 작업을 수행하는 AI 시스템을 말합니다. 단순히 질문에 답하는 AI보다 한 단계 더 &amp;ldquo;행동하는 AI&amp;rdquo;에 가깝습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쉽게 말하면 ChatGPT가 &amp;ldquo;질문에 답하는 AI&amp;rdquo;에 가깝다면, AI 에이전트는 &amp;ldquo;목표를 받고 작업을 진행하는 AI&amp;rdquo;에 가깝습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2026년 5월 기준으로 AI 에이전트는 생성형 AI 분야에서 자주 언급되는 개념입니다. 하지만 막상 설명을 들으면 어렵게 느껴질 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ChatGPT랑 뭐가 다른 걸까?&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;이 글에서는 AI 에이전트의 뜻, 작동 방식, ChatGPT와의 차이, 실제 활용 예시, 주의할 점을 초보자도 이해하기 쉽게 정리합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AI 에이전트란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트는 쉽게 말해 &lt;b&gt;목표를 받고, 스스로 다음 행동을 정해, 작업을 수행하는 AI 시스템&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 사용자가 이렇게 요청했다고 가정해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;gt; &amp;ldquo;다음 주 제주도 2박 3일 여행 일정을 짜줘.&amp;rdquo;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 챗봇은 여행 일정을 텍스트로 추천해줄 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 AI 에이전트는 더 넓은 작업을 처리하도록 설계될 수 있습니다. 예를 들면 항공권 가격을 확인하고, 숙소 후보를 비교하고, 지도 정보를 참고해 동선을 짜고, 사용자의 조건에 맞게 일정을 다시 조정하는 식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 실제로 어디까지 가능한지는 사용하는 서비스, 연결된 도구, 권한 설정에 따라 달라집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 하나입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AI 에이전트는 단순히 답변만 하는 것이 아니라, 목표를 이루기 위해 여러 단계를 진행할 수 있는 AI입니다.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ChatGPT와 AI 에이전트는 무엇이 다를까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보자가 가장 많이 헷갈리는 부분이 이 지점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChatGPT 같은 AI 챗봇은 사용자의 질문에 답을 생성합니다. 예를 들어 &amp;ldquo;블로그 글 목차를 짜줘&amp;rdquo;라고 하면 목차를 만들어줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트는 여기서 한 단계 더 나아갑니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목차를 만드는 데 그치지 않고, 필요한 자료를 찾고, 초안을 작성하고, 오류를 점검하고, 수정 방향을 판단하는 식으로 작업 흐름을 이어갈 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단히 비교하면 다음과 같습니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;일반 AI 챗봇&lt;/th&gt;
&lt;th&gt;AI 에이전트&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;중심 기능&lt;/td&gt;
&lt;td&gt;질문에 답변&lt;/td&gt;
&lt;td&gt;목표 달성을 위한 작업 수행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;작동 방식&lt;/td&gt;
&lt;td&gt;입력 &amp;rarr; 답변&lt;/td&gt;
&lt;td&gt;목표 &amp;rarr; 계획 &amp;rarr; 실행 &amp;rarr; 점검&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;도구 사용&lt;/td&gt;
&lt;td&gt;제한적이거나 없음&lt;/td&gt;
&lt;td&gt;검색, 파일, API, 앱 등 도구 사용 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;예시&lt;/td&gt;
&lt;td&gt;글 요약하기&lt;/td&gt;
&lt;td&gt;자료 조사 후 글 작성하고 검토하기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;사람의 역할&lt;/td&gt;
&lt;td&gt;질문 입력&lt;/td&gt;
&lt;td&gt;목표 설정과 결과 검토&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 모든 AI 서비스가 명확하게 둘로 나뉘는 것은 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근에는 챗봇 안에 에이전트 기능이 들어가기도 하고, 에이전트라고 부르지만 실제로는 단순 자동화에 가까운 경우도 있습니다. 그래서 이름보다 중요한 것은 &lt;b&gt;그 AI가 실제로 무엇을 할 수 있는지&lt;/b&gt;입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AI 에이전트는 어떻게 작동할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트의 작동 방식은 보통 다음 흐름으로 이해하면 쉽습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 목표를 이해한다&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;ldquo;블로그 글을 작성해줘&amp;rdquo;라는 요청이 들어오면, AI 에이전트는 단순히 글만 쓰는 것이 아니라 주제, 독자, 검색 의도, 필요한 자료를 함께 판단할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 작업을 나눈다&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;ldquo;AI 에이전트 블로그 글 작성&amp;rdquo;이라는 목표가 있다면 다음과 같이 나눌 수 있습니다.&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;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;이렇게 큰 일을 작은 단계로 쪼개면 AI가 더 체계적으로 작업할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 필요한 도구를 사용한다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트의 중요한 특징 중 하나는 도구 사용입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 모델 자체가 모든 일을 직접 처리하는 것은 아닙니다. 대신 필요한 도구와 연결되면 실제 작업 범위가 넓어집니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;연결 도구&lt;/th&gt;
&lt;th&gt;AI 에이전트가 할 수 있는 일&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;웹 검색&lt;/td&gt;
&lt;td&gt;최신 자료 조사, 뉴스 확인, 가격 비교&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;캘린더&lt;/td&gt;
&lt;td&gt;빈 시간 확인, 일정 후보 정리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;이메일&lt;/td&gt;
&lt;td&gt;메일 초안 작성, 발송 전 검토 요청&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;파일 도구&lt;/td&gt;
&lt;td&gt;문서 읽기, 요약, 정리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;코드 실행 도구&lt;/td&gt;
&lt;td&gt;데이터 계산, 간단한 분석 자동화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;외부 API&lt;/td&gt;
&lt;td&gt;서비스 간 데이터 조회와 작업 연결&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &amp;ldquo;회의 일정을 잡아줘&amp;rdquo;라는 요청에서 캘린더 도구와 이메일 도구가 연결되어 있다면, 빈 시간을 확인하고 참석자에게 안내 메일 초안을 작성하는 식으로 이어질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 이메일 발송, 파일 수정, 결제처럼 되돌리기 어려운 작업은 자동 실행보다 &lt;b&gt;사용자 확인 후 실행&lt;/b&gt; 방식이 안전합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 결과를 확인하고 수정한다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트는 한 번에 끝내는 것이 아니라 결과를 확인하고 다시 수정하는 방식으로 작동할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 글을 작성한 뒤 문장이 너무 어렵다고 판단하면 더 쉽게 고칠 수 있습니다. 자료가 부족하다고 판단하면 추가 조사를 할 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 점 때문에 AI 에이전트는 단순 응답형 AI보다 &amp;ldquo;작업 흐름&amp;rdquo;에 더 가깝습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;초보자가 이해하기 쉬운 AI 에이전트 예시&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트는 아직 추상적으로 느껴질 수 있습니다. 그래서 일상적인 예시로 보는 것이 좋습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예시 1. 여행 일정을 짜주는 AI 에이전트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 &amp;ldquo;부산 1박 2일 여행 계획을 세워줘&amp;rdquo;라고 요청합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트는 다음 작업을 할 수 있습니다.&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;li&gt;음식점 후보 정리&lt;/li&gt;
&lt;li&gt;예산에 맞게 일정 조정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 &amp;ldquo;부산 여행지는 해운대, 광안리, 감천문화마을이 있습니다&amp;rdquo;라고 답하는 것보다 더 구체적입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예시 2. 블로그 글 작성을 돕는 AI 에이전트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그 운영자가 &amp;ldquo;AI 에이전트란 주제로 초보자용 글을 써줘&amp;rdquo;라고 요청합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트는 다음 단계를 수행할 수 있습니다.&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;li&gt;본문 작성&lt;/li&gt;
&lt;li&gt;문장 검토&lt;/li&gt;
&lt;li&gt;SEO 요소 점검&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 AI 에이전트는 단순 글쓰기 도구가 아니라 콘텐츠 제작 흐름을 도와주는 시스템에 가깝습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;예시 3. 회의록과 업무 정리를 돕는 AI 에이전트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직장인이 &amp;ldquo;이번 주 회의 내용을 정리하고 할 일을 뽑아줘&amp;rdquo;라고 요청합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트는 다음과 같은 작업을 할 수 있습니다.&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;li&gt;일정 등록 또는 알림 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 방식으로 반복적인 사무 작업을 줄이는 데 활용될 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AI 에이전트가 주목받는 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트가 주목받는 이유는 단순합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 AI가 &amp;ldquo;답변&amp;rdquo;에 강했다면, AI 에이전트는 &amp;ldquo;업무 흐름&amp;rdquo;에 더 가까워졌기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자는 매번 세세하게 지시하지 않아도 됩니다. 목표를 주면 AI가 필요한 단계를 나누고, 중간 작업을 처리하고, 결과물을 만들어낼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 다음 분야에서 관심이 큽니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;고객 응대&lt;/li&gt;
&lt;li&gt;일정 관리&lt;/li&gt;
&lt;li&gt;자료 조사&lt;/li&gt;
&lt;li&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;/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;다만 이 기대가 항상 현실과 일치하는 것은 아닙니다. AI 에이전트가 제대로 작동하려면 좋은 도구 연결, 명확한 권한 설정, 검증 절차가 필요합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AI 에이전트와 자동화 프로그램은 같은 걸까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비슷하지만 완전히 같지는 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 자동화 프로그램은 정해진 규칙대로 움직입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &amp;ldquo;매일 오전 9시에 보고서를 이메일로 보내기&amp;rdquo; 같은 작업은 전통적인 자동화에 가깝습니다. 조건과 동작이 미리 정해져 있기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트는 상황을 해석하고 다음 행동을 선택할 수 있다는 점이 다릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 보고서 내용이 부족하면 추가 자료를 찾고, 일정이 충돌하면 다른 시간을 제안하고, 사용자의 목적에 맞게 결과물을 조정할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 다음과 같습니다.&lt;/p&gt;
&lt;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;AI 챗봇: 질문에 답변 생성&lt;/li&gt;
&lt;li&gt;AI 에이전트: 목표를 이해하고 계획을 세워 작업 수행&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 실제 서비스에서는 이 경계가 섞여 있는 경우가 많습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AI 에이전트의 한계와 주의할 점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트는 유용할 수 있지만, 모든 일을 믿고 맡겨도 되는 도구는 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 초보자는 다음 한계를 알고 있어야 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 잘못된 판단을 할 수 있다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI는 그럴듯한 답을 만들 수 있지만, 항상 정확한 것은 아닙니다. 자료 해석을 잘못하거나, 오래된 정보를 기준으로 판단하거나, 맥락을 놓칠 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 업무에서는 사람이 반드시 결과를 확인해야 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 권한 설정이 중요하다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트가 이메일, 캘린더, 결제, 파일 시스템과 연결된다면 편리해집니다. 하지만 그만큼 권한 관리가 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 AI가 마음대로 메일을 보내거나, 파일을 수정하거나, 외부 서비스에 접근할 수 있다면 문제가 생길 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 사용할 때는 자동 실행보다 검토 후 실행 방식이 안전합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 개인정보와 보안에 주의해야 한다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트에게 민감한 정보를 입력할 때는 주의가 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사 내부 자료, 고객 정보, 계약서, 계정 정보, 결제 정보 등은 서비스의 보안 정책과 데이터 처리 방식을 확인한 뒤 사용하는 것이 좋습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 모든 작업에 적합한 것은 아니다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트는 반복적이고 구조화된 작업에는 유용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 높은 책임이 필요한 의사결정, 법률&amp;middot;의료&amp;middot;재무 판단, 보안상 민감한 작업에서는 보조 도구로 보는 것이 안전합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 그대로 믿기보다 참고 자료로 활용하는 태도가 필요합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AI 에이전트를 처음 써볼 때의 기준&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트를 처음 접한다면 &amp;ldquo;무엇까지 자동으로 맡길 수 있을까?&amp;rdquo;보다 &amp;ldquo;어디까지 사람이 확인해야 할까?&amp;rdquo;를 먼저 생각하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 다음 기준으로 접근하면 안전합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정보 조사, 요약, 초안 작성처럼 되돌리기 쉬운 작업부터 맡긴다.&lt;/li&gt;
&lt;li&gt;이메일 발송, 파일 수정, 결제처럼 영향이 큰 작업은 승인 후 실행으로 둔다.&lt;/li&gt;
&lt;li&gt;결과가 맞는지 사람이 최종 확인한다.&lt;/li&gt;
&lt;li&gt;민감한 정보는 입력 전에 서비스의 데이터 처리 방식을 확인한다.&lt;/li&gt;
&lt;li&gt;처음부터 완전 자동화하려 하지 말고, 반복 작업 일부를 줄이는 방식으로 시작한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트는 사람의 판단을 완전히 대체하는 도구라기보다, 반복적인 중간 작업을 줄여주는 보조 시스템에 가깝습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AI 에이전트를 이해할 때 기억할 핵심&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트를 어렵게 생각할 필요는 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 다음 한 문장으로 정리할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AI 에이전트는 목표를 받고, 계획을 세우고, 도구를 사용해 작업을 수행하는 AI 시스템입니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순 챗봇은 질문에 답합니다. AI 에이전트는 목표를 달성하기 위한 과정을 진행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &amp;ldquo;요약해줘&amp;rdquo;는 챗봇도 잘할 수 있습니다. 하지만 &amp;ldquo;자료를 찾고, 비교하고, 정리해서 보고서 형태로 만들어줘&amp;rdquo;는 에이전트에 가까운 작업입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 AI 서비스는 단순히 대화하는 형태에서 벗어나, 사용자의 일을 대신 처리하는 방향으로 더 발전할 가능성이 큽니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 AI 에이전트가 사람의 판단을 완전히 대체한다고 보기는 어렵습니다. 현실적인 활용 방식은 사람이 목표를 정하고, AI가 중간 작업을 돕고, 사람이 최종 결과를 검토하는 구조에 가깝습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트를 제대로 이해하려면 &amp;ldquo;AI가 다 알아서 한다&amp;rdquo;가 아니라, &lt;b&gt;사람이 지시하고 AI가 실행을 보조하는 작업 파트너&lt;/b&gt;로 보는 것이 좋습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;FAQ&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AI 에이전트는 ChatGPT와 같은 건가요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완전히 같지는 않습니다. ChatGPT는 대화형 AI 서비스이고, AI 에이전트는 목표를 수행하기 위해 계획을 세우고 도구를 사용할 수 있는 시스템을 뜻합니다. 다만 ChatGPT 같은 서비스 안에 에이전트 기능이 포함될 수는 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AI 에이전트를 처음 써볼 때 어떤 작업부터 맡기면 좋나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정보 조사, 문서 요약, 글 초안 작성, 회의록 정리처럼 사람이 쉽게 검토할 수 있는 작업부터 시작하는 것이 좋습니다. 반대로 결제, 메일 자동 발송, 파일 삭제처럼 되돌리기 어려운 작업은 처음부터 자동화하지 않는 편이 안전합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AI 에이전트가 만든 결과는 그대로 써도 되나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 결과물이라면 그대로 쓰기보다 확인 과정을 거치는 것이 좋습니다. 특히 최신 정보, 숫자, 법률&amp;middot;의료&amp;middot;재무 관련 내용, 회사 내부 자료와 연결된 작업은 사람이 검토해야 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AI 에이전트는 어디에 활용할 수 있나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자료 조사, 일정 관리, 고객 응대, 블로그 글 작성, 코드 작성, 데이터 분석, 반복 업무 자동화 등에 활용될 수 있습니다. 다만 서비스마다 가능한 기능과 권한 범위가 다르므로 실제 사용 전 확인이 필요합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AI 에이전트를 이해할 때 가장 중요한 점은 무엇인가요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI 에이전트는 &amp;ldquo;질문에 답하는 도구&amp;rdquo;를 넘어 &amp;ldquo;목표를 처리하도록 설계된 시스템&amp;rdquo;에 가깝다는 점입니다. 다만 최종 판단과 책임은 여전히 사람이 가져야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI</category>
      <category>AI기초</category>
      <category>AI에이전트</category>
      <category>chatGPT</category>
      <category>생성형AI</category>
      <category>업무자동화</category>
      <category>인공지능</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/32</guid>
      <comments>https://notebase.tistory.com/entry/what-is-ai-agent-beginner-guide#entry32comment</comments>
      <pubDate>Fri, 22 May 2026 10:45:46 +0900</pubDate>
    </item>
    <item>
      <title>파이썬 모듈과 패키지: import로 코드를 나누고 재사용하는 방법</title>
      <link>https://notebase.tistory.com/entry/python-modules-packages-guide</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 모듈과 패키지의 차이, import 사용법, 표준 라이브러리와 외부 패키지 설치 개념을 초보자 기준으로 정리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 모듈과 패키지는 코드를 여러 파일로 나누고 다시 가져와 쓰기 위한 구조입니다. 코드가 길어질수록 한 파일에 모든 내용을 넣기 어렵기 때문에, 기능별로 나누는 방법을 알아야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 &lt;code&gt;import&lt;/code&gt;가 낯설 수 있습니다. 하지만 핵심은 단순합니다. 이미 만들어둔 코드를 현재 파일에서 불러와 사용하는 것입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;모듈이란 무엇일까&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서 모듈은 보통 하나의 &lt;code&gt;.py&lt;/code&gt; 파일을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &lt;code&gt;calculator.py&lt;/code&gt;라는 파일을 만들었다고 하겠습니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;# calculator.py

def add(a, b):
    return a + b

def subtract(a, b):
    return a - b&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일은 계산 기능을 담은 모듈로 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 파이썬 파일에서 이 모듈을 불러올 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;import calculator

print(calculator.add(3, 5))
print(calculator.subtract(10, 4))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;8
6&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;code&gt;import calculator&lt;/code&gt;는 &lt;code&gt;calculator.py&lt;/code&gt; 파일을 가져온다는 뜻입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈 안의 함수를 사용할 때는 &lt;code&gt;calculator.add()&lt;/code&gt;처럼 모듈 이름을 함께 붙입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;import 기본 사용법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 기본적인 import 방식은 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;elm&quot;&gt;&lt;code&gt;import 모듈이름&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;code&gt;math&lt;/code&gt; 모듈을 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;lua&quot;&gt;&lt;code&gt;import math

print(math.sqrt(16))
print(math.pi)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 비슷합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;4.0
3.141592653589793&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;code&gt;math.sqrt()&lt;/code&gt;는 제곱근을 구하는 함수입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;math.pi&lt;/code&gt;는 원주율 값을 나타냅니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;from import 사용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈 전체가 아니라 특정 함수만 가져올 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;from math import sqrt

print(sqrt(16))&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;code&gt;math.sqrt()&lt;/code&gt;가 아니라 바로 &lt;code&gt;sqrt()&lt;/code&gt;라고 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 개를 가져올 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;from math import sqrt, pi

print(sqrt(25))
print(pi)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 가져오는 이름이 많아지면 어디에서 온 함수인지 헷갈릴 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보 단계에서는 &lt;code&gt;import math&lt;/code&gt;처럼 모듈 이름을 붙여 사용하는 방식이 더 명확할 때가 많습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;as로 이름 줄이기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈 이름이 길거나 자주 사용할 때는 &lt;code&gt;as&lt;/code&gt;로 별명을 붙일 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;import random as rd

print(rd.randint(1, 10))&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;code&gt;random&lt;/code&gt; 모듈을 &lt;code&gt;rd&lt;/code&gt;라는 이름으로 사용한 예입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무에서는 자주 쓰이는 별명이 있습니다.&lt;/p&gt;
&lt;pre class=&quot;elm&quot;&gt;&lt;code&gt;import pandas as pd
import numpy as np&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;h2 data-ke-size=&quot;size26&quot;&gt;표준 라이브러리란 무엇일까&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬은 설치할 때 기본으로 제공되는 모듈이 많습니다. 이를 표준 라이브러리라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 예는 다음과 같습니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;모듈&lt;/th&gt;
&lt;th&gt;용도&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;math&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;수학 계산&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;random&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;무작위 값 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;datetime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;날짜와 시간 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;os&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;운영체제 관련 기능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sys&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;파이썬 실행 환경 관련 기능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 무작위 숫자를 만들고 싶다면 &lt;code&gt;random&lt;/code&gt; 모듈을 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;import random

number = random.randint(1, 6)
print(number)&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;1부터 6 사이의 정수가 무작위로 출력됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주사위 기능을 만들 때 사용할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;직접 만든 모듈 사용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 폴더에 두 파일이 있다고 가정하겠습니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;project/
├── main.py
└── greeting.py&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;code&gt;greeting.py&lt;/code&gt; 파일에는 함수를 작성합니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;# greeting.py

def hello(name):
    return name + &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;code&gt;main.py&lt;/code&gt;에서는 이 함수를 가져와 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;# main.py

import greeting

message = greeting.hello(&quot;민수&quot;)
print(message)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;민수님, 안녕하세요&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 기능별로 파일을 나누면 코드 관리가 쉬워집니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;패키지란 무엇일까&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패키지는 여러 모듈을 묶어놓은 폴더 구조입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 구조가 있을 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;my_package/
├── __init__.py
├── calculator.py
└── greeting.py&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;code&gt;my_package&lt;/code&gt;는 패키지입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 안의 &lt;code&gt;calculator.py&lt;/code&gt;, &lt;code&gt;greeting.py&lt;/code&gt;는 모듈입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패키지 안의 모듈을 사용할 때는 다음처럼 가져올 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;from my_package import calculator

print(calculator.add(3, 5))&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;ldquo;모듈은 파이썬 파일, 패키지는 모듈을 담는 폴더&amp;rdquo;라고 이해하면 됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;__init__.py는 무엇일까&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;패키지 예제에서 &lt;code&gt;__init__.py&lt;/code&gt; 파일이 보였을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일은 해당 폴더를 파이썬 패키지로 인식하게 하는 역할을 해왔습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 파이썬에서는 일부 경우 &lt;code&gt;__init__.py&lt;/code&gt;가 없어도 패키지처럼 동작할 수 있습니다. 하지만 초보 단계에서는 패키지 폴더에 &lt;code&gt;__init__.py&lt;/code&gt;를 두는 방식으로 익히는 것이 이해하기 쉽습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 파일 내용이 비어 있어도 괜찮습니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;# __init__.py&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;외부 패키지란 무엇일까&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에는 기본 제공 모듈 외에도 다른 개발자들이 만든 패키지를 설치해서 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 웹 요청을 쉽게 처리하는 &lt;code&gt;requests&lt;/code&gt;, 데이터 분석에 자주 쓰이는 &lt;code&gt;pandas&lt;/code&gt; 같은 패키지가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 패키지는 보통 &lt;code&gt;pip&lt;/code&gt; 명령어로 설치합니다.&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;pip install requests&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;설치한 뒤에는 파이썬 코드에서 import해서 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;elm&quot;&gt;&lt;code&gt;import requests&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;h2 data-ke-size=&quot;size26&quot;&gt;import할 때 자주 하는 실수&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 파일 이름을 표준 모듈과 같게 짓기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 파일 이름을 &lt;code&gt;random.py&lt;/code&gt;로 만들면 파이썬의 표준 &lt;code&gt;random&lt;/code&gt; 모듈과 충돌할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;random.py&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 이름은 피하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;my_random_test.py&lt;/code&gt;처럼 더 구체적인 이름을 사용하면 충돌을 줄일 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 모듈 이름에 하이픈 사용하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 파일 이름에 하이픈을 사용하면 import하기 어렵습니다.&lt;/p&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;my-module.py&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이보다는 밑줄을 사용하는 것이 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;my_module.py&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 설치하지 않은 외부 패키지를 import하기&lt;/h3&gt;
&lt;pre class=&quot;elm&quot;&gt;&lt;code&gt;import requests&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;code&gt;requests&lt;/code&gt;가 설치되어 있어야 동작합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치되어 있지 않다면 &lt;code&gt;ModuleNotFoundError&lt;/code&gt;가 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴 때는 터미널에서 패키지를 설치해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;pip install requests&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;모듈을 나누는 기준&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 파일을 너무 많이 나눌 필요는 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드가 짧을 때는 하나의 파일로도 충분합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 아래와 같은 상황이라면 모듈 분리를 생각해볼 수 있습니다.&lt;/p&gt;
&lt;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;모듈은 코드를 더 복잡하게 만드는 도구가 아니라, 복잡해진 코드를 정리하기 위한 도구입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 모듈은 하나의 &lt;code&gt;.py&lt;/code&gt; 파일입니다. 패키지는 여러 모듈을 담은 폴더 구조입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;import&lt;/code&gt;를 사용하면 다른 파일이나 파이썬이 제공하는 기능을 현재 코드에서 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보 단계에서는 먼저 &lt;code&gt;import math&lt;/code&gt;, &lt;code&gt;import random&lt;/code&gt; 같은 표준 라이브러리 사용법부터 익히는 것이 좋습니다. 이후 직접 만든 파일을 모듈로 불러오고, 코드가 많아지면 패키지 구조로 나누면 됩니다.&lt;/p&gt;</description>
      <category>개발/python</category>
      <category>import</category>
      <category>Python</category>
      <category>모듈</category>
      <category>코딩입문</category>
      <category>파이썬</category>
      <category>패키지</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/31</guid>
      <comments>https://notebase.tistory.com/entry/python-modules-packages-guide#entry31comment</comments>
      <pubDate>Fri, 22 May 2026 10:17:44 +0900</pubDate>
    </item>
    <item>
      <title>파이썬 파일 입출력: 텍스트 파일 읽고 쓰는 기본 방법</title>
      <link>https://notebase.tistory.com/entry/python-file-io-guide</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서 텍스트 파일을 읽고 쓰는 방법을 open, with, read, readline, write 예제로 초보자 기준에서 정리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 파일 입출력은 프로그램 밖에 있는 파일을 읽거나, 프로그램에서 만든 내용을 파일로 저장하는 방법입니다. 메모장에 적힌 내용을 읽거나, 계산 결과를 텍스트 파일로 저장할 때 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램 안의 변수는 실행이 끝나면 사라집니다. 하지만 파일로 저장하면 나중에 다시 열어 사용할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;파일 입출력이 필요한 상황&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 경우 파일 입출력이 필요합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자가 입력한 내용을 저장할 때&lt;/li&gt;
&lt;li&gt;프로그램 실행 결과를 기록할 때&lt;/li&gt;
&lt;li&gt;텍스트 파일에 있는 데이터를 읽어올 때&lt;/li&gt;
&lt;li&gt;로그 파일을 만들 때&lt;/li&gt;
&lt;li&gt;설정 파일을 불러올 때&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보 단계에서는 먼저 텍스트 파일을 읽고 쓰는 방법부터 익히면 충분합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;open 함수 기본 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서 파일을 다룰 때는 &lt;code&gt;open()&lt;/code&gt; 함수를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;file = open(&quot;sample.txt&quot;, &quot;r&quot;, encoding=&quot;utf-8&quot;)
content = file.read()
print(content)
file.close()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 &lt;code&gt;sample.txt&lt;/code&gt; 파일을 읽어서 화면에 출력합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 부분은 세 가지입니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;코드&lt;/th&gt;
&lt;th&gt;의미&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sample.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;열 파일 이름&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;r&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;읽기 모드&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;encoding=&quot;utf-8&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;한글 처리를 위한 인코딩 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일을 열었으면 마지막에 &lt;code&gt;close()&lt;/code&gt;로 닫아야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 매번 직접 닫는 방식은 실수하기 쉽습니다. 그래서 보통은 &lt;code&gt;with&lt;/code&gt; 문을 사용합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;with 문으로 파일 열기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 입출력에서는 &lt;code&gt;with&lt;/code&gt; 문을 사용하는 방식이 일반적입니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;with open(&quot;sample.txt&quot;, &quot;r&quot;, encoding=&quot;utf-8&quot;) as file:
    content = file.read()
    print(content)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;with&lt;/code&gt; 문을 사용하면 파일을 직접 닫지 않아도 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 블록이 끝나면 파이썬이 자동으로 파일을 닫아줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 초보자라면 파일을 다룰 때 &lt;code&gt;with open(...) as file:&lt;/code&gt; 형태를 기본으로 사용하는 것이 좋습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;파일 읽기 모드&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일을 읽을 때는 &lt;code&gt;r&lt;/code&gt; 모드를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;with open(&quot;memo.txt&quot;, &quot;r&quot;, encoding=&quot;utf-8&quot;) as file:
    text = file.read()
    print(text)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;read()&lt;/code&gt;는 파일 전체 내용을 하나의 문자열로 읽습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &lt;code&gt;memo.txt&lt;/code&gt; 파일 내용이 다음과 같다고 가정하겠습니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;파이썬 공부
파일 입출력 연습&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;파이썬 공부
파일 입출력 연습&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 전체를 한 번에 읽고 싶을 때 &lt;code&gt;read()&lt;/code&gt;를 사용합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;한 줄씩 읽기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일을 한 줄씩 읽고 싶을 때는 &lt;code&gt;readline()&lt;/code&gt;을 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;sas&quot;&gt;&lt;code&gt;with open(&quot;memo.txt&quot;, &quot;r&quot;, encoding=&quot;utf-8&quot;) as file:
    line1 = file.readline()
    line2 = file.readline()

print(line1)
print(line2)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;readline()&lt;/code&gt;은 호출할 때마다 한 줄씩 읽습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제로는 반복문과 함께 파일을 읽는 방식이 더 자주 사용됩니다.&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;with open(&quot;memo.txt&quot;, &quot;r&quot;, encoding=&quot;utf-8&quot;) as file:
    for line in file:
        print(line.strip())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;strip()&lt;/code&gt;은 줄 끝의 줄바꿈 문자나 앞뒤 공백을 제거할 때 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일을 줄 단위로 처리해야 한다면 이 방식이 읽기 좋습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;여러 줄을 리스트로 읽기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;readlines()&lt;/code&gt;를 사용하면 파일의 각 줄을 리스트로 가져올 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;with open(&quot;memo.txt&quot;, &quot;r&quot;, encoding=&quot;utf-8&quot;) as file:
    lines = file.readlines()

print(lines)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 비슷합니다.&lt;/p&gt;
&lt;pre class=&quot;scheme&quot;&gt;&lt;code&gt;['파이썬 공부\n', '파일 입출력 연습\n']&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 줄이 리스트의 요소로 들어갑니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;줄 끝에 &lt;code&gt;\n&lt;/code&gt;이 포함될 수 있다는 점을 기억해야 합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;파일 쓰기 모드&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일에 내용을 저장할 때는 &lt;code&gt;w&lt;/code&gt; 모드를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;with open(&quot;result.txt&quot;, &quot;w&quot;, encoding=&quot;utf-8&quot;) as file:
    file.write(&quot;파이썬 파일 쓰기 연습&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드를 실행하면 &lt;code&gt;result.txt&lt;/code&gt; 파일이 만들어지고 내용이 저장됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;w&lt;/code&gt; 모드는 기존 파일 내용이 있으면 모두 지우고 새로 씁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 이미 &lt;code&gt;result.txt&lt;/code&gt;에 내용이 있었더라도 위 코드를 실행하면 기존 내용은 사라집니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;줄바꿈 넣어서 쓰기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;write()&lt;/code&gt;는 자동으로 줄바꿈을 넣어주지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;줄을 나누고 싶다면 &lt;code&gt;\n&lt;/code&gt;을 직접 넣어야 합니다.&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;with open(&quot;result.txt&quot;, &quot;w&quot;, encoding=&quot;utf-8&quot;) as file:
    file.write(&quot;첫 번째 줄\n&quot;)
    file.write(&quot;두 번째 줄\n&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저장된 파일 내용은 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;첫 번째 줄
두 번째 줄&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;파일에 내용 추가하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 내용을 지우지 않고 뒤에 추가하고 싶다면 &lt;code&gt;a&lt;/code&gt; 모드를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;with open(&quot;result.txt&quot;, &quot;a&quot;, encoding=&quot;utf-8&quot;) as file:
    file.write(&quot;추가된 줄\n&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;a&lt;/code&gt;는 append의 의미입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 끝에 새로운 내용이 추가됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;파일 모드 정리&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;모드&lt;/th&gt;
&lt;th&gt;의미&lt;/th&gt;
&lt;th&gt;특징&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;r&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;읽기&lt;/td&gt;
&lt;td&gt;파일이 없으면 오류 발생&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;w&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;쓰기&lt;/td&gt;
&lt;td&gt;기존 내용을 지우고 새로 작성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;추가&lt;/td&gt;
&lt;td&gt;기존 내용 뒤에 이어서 작성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;x&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;새 파일 생성&lt;/td&gt;
&lt;td&gt;파일이 이미 있으면 오류 발생&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보 단계에서는 &lt;code&gt;r&lt;/code&gt;, &lt;code&gt;w&lt;/code&gt;, &lt;code&gt;a&lt;/code&gt; 세 가지만 먼저 익히면 충분합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;파일이 없을 때 발생하는 오류&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽기 모드에서 파일이 없으면 오류가 발생합니다.&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;with open(&quot;없는파일.txt&quot;, &quot;r&quot;, encoding=&quot;utf-8&quot;) as file:
    content = file.read()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 &lt;code&gt;FileNotFoundError&lt;/code&gt;가 발생할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일이 없을 가능성이 있다면 예외 처리를 함께 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;try:
    with open(&quot;memo.txt&quot;, &quot;r&quot;, encoding=&quot;utf-8&quot;) as file:
        content = file.read()
        print(content)
except FileNotFoundError:
    print(&quot;파일을 찾을 수 없습니다.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일 입출력은 예외 처리와 함께 쓰이는 경우가 많습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;간단한 메모 저장 예제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 입력한 내용을 파일에 저장하는 예제를 보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;memo = input(&quot;메모를 입력하세요: &quot;)

with open(&quot;memo.txt&quot;, &quot;w&quot;, encoding=&quot;utf-8&quot;) as file:
    file.write(memo)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력한 메모가 &lt;code&gt;memo.txt&lt;/code&gt; 파일에 저장됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 저장된 파일을 다시 읽어보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;with open(&quot;memo.txt&quot;, &quot;r&quot;, encoding=&quot;utf-8&quot;) as file:
    memo = file.read()

print(&quot;저장된 메모:&quot;, memo)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 파일에 저장하면 프로그램을 종료한 뒤에도 데이터를 다시 사용할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;인코딩을 지정해야 하는 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한글이 들어간 파일을 다룰 때는 &lt;code&gt;encoding=&quot;utf-8&quot;&lt;/code&gt;을 지정하는 것이 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;with open(&quot;memo.txt&quot;, &quot;r&quot;, encoding=&quot;utf-8&quot;) as file:
    content = file.read()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인코딩 설정이 맞지 않으면 한글이 깨지거나 오류가 발생할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 윈도우 환경에서는 인코딩 문제를 만나는 경우가 있으므로 초보 단계부터 &lt;code&gt;encoding=&quot;utf-8&quot;&lt;/code&gt;을 쓰는 습관을 들이는 것이 좋습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 파일 입출력은 파일을 읽고 쓰기 위한 기본 문법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일을 열 때는 &lt;code&gt;open()&lt;/code&gt;을 사용하고, 가능하면 &lt;code&gt;with&lt;/code&gt; 문과 함께 사용하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽을 때는 &lt;code&gt;r&lt;/code&gt;, 새로 쓸 때는 &lt;code&gt;w&lt;/code&gt;, 기존 내용 뒤에 추가할 때는 &lt;code&gt;a&lt;/code&gt; 모드를 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;텍스트 파일을 다룰 때는 한글 깨짐을 줄이기 위해 &lt;code&gt;encoding=&quot;utf-8&quot;&lt;/code&gt;을 함께 지정하는 편이 안전합니다.&lt;/p&gt;</description>
      <category>개발/python</category>
      <category>open</category>
      <category>Python</category>
      <category>코딩입문</category>
      <category>파이썬</category>
      <category>파일입출력</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/30</guid>
      <comments>https://notebase.tistory.com/entry/python-file-io-guide#entry30comment</comments>
      <pubDate>Fri, 22 May 2026 10:05:51 +0900</pubDate>
    </item>
    <item>
      <title>파이썬 예외 처리: 오류가 나도 프로그램이 멈추지 않게 하는 방법</title>
      <link>https://notebase.tistory.com/entry/python-exception-handling-guide</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 예외 처리의 기본 구조와 try, except, else, finally 사용법을 초보자 기준 예제로 정리합니다.&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;파이썬 예외 처리는 프로그램 실행 중 발생할 수 있는 오류를 안전하게 처리하는 방법입니다. 파일이 없거나, 숫자로 바꿀 수 없는 값이 들어오거나, 0으로 나누는 상황에서 프로그램이 바로 종료되지 않도록 도와줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 작성하다 보면 오류는 피할 수 없습니다. 중요한 것은 오류가 났을 때 프로그램이 어떻게 반응하게 할지 정하는 것입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;오류와 예외는 무엇이 다를까&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서 오류는 크게 두 가지로 나눠볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째는 문법 오류입니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;print(&quot;안녕하세요&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;괄호를 닫지 않았기 때문에 코드 자체가 실행되지 않습니다. 이런 오류는 실행 전에 고쳐야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째는 실행 중 발생하는 예외입니다.&lt;/p&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;number = int(&quot;abc&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문법은 맞지만 &lt;code&gt;&quot;abc&quot;&lt;/code&gt;는 숫자로 바꿀 수 없습니다. 이 코드는 실행 중 &lt;code&gt;ValueError&lt;/code&gt;가 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예외 처리는 주로 이런 실행 중 오류를 다룹니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;예외 처리가 필요한 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자에게 숫자를 입력받는 코드를 생각해보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;age = int(input(&quot;나이를 입력하세요: &quot;))
print(age + 1)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 &lt;code&gt;20&lt;/code&gt;을 입력하면 문제없이 동작합니다. 하지만 &lt;code&gt;스무살&lt;/code&gt;이라고 입력하면 오류가 발생하고 프로그램이 종료됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 상황에서 예외 처리를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;try:
    age = int(input(&quot;나이를 입력하세요: &quot;))
    print(age + 1)
except ValueError:
    print(&quot;숫자로 입력해야 합니다.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 숫자가 아닌 값을 입력해도 프로그램이 바로 종료되지 않고 안내 문구를 출력합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;try except 기본 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예외 처리의 기본 구조는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;delphi&quot;&gt;&lt;code&gt;try:
    예외가 발생할 수 있는 코드
except 예외종류:
    예외가 발생했을 때 실행할 코드&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;try:
    result = 10 / 0
except ZeroDivisionError:
    print(&quot;0으로 나눌 수 없습니다.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;0으로 나눌 수 없습니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;10 / 0&lt;/code&gt;은 실행할 수 없는 계산입니다. 그래서 &lt;code&gt;ZeroDivisionError&lt;/code&gt;가 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 &lt;code&gt;except ZeroDivisionError&lt;/code&gt; 아래 코드가 실행됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;여러 예외 처리하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 코드에서 여러 종류의 예외가 발생할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;try:
    number = int(input(&quot;숫자를 입력하세요: &quot;))
    result = 10 / number
    print(result)
except ValueError:
    print(&quot;숫자만 입력할 수 있습니다.&quot;)
except ZeroDivisionError:
    print(&quot;0으로 나눌 수 없습니다.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 두 가지 예외를 처리합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;숫자로 바꿀 수 없는 값이 들어오면 &lt;code&gt;ValueError&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;0을 입력하면 &lt;code&gt;ZeroDivisionError&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예외 종류를 나눠 쓰면 사용자에게 더 정확한 안내를 할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;모든 예외를 한 번에 처리할 수도 있다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래처럼 예외 종류를 쓰지 않고 처리할 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;try:
    number = int(input(&quot;숫자를 입력하세요: &quot;))
    print(10 / number)
except:
    print(&quot;오류가 발생했습니다.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 방식은 주의해야 합니다. 어떤 오류가 발생했는지 알기 어렵기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보 단계에서도 가능한 한 구체적인 예외 이름을 쓰는 습관이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;권장되는 방식은 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;try:
    number = int(input(&quot;숫자를 입력하세요: &quot;))
    print(10 / number)
except ValueError:
    print(&quot;숫자만 입력해주세요.&quot;)
except ZeroDivisionError:
    print(&quot;0은 입력할 수 없습니다.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;예외 메시지 확인하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예외가 발생했을 때 파이썬이 제공하는 메시지를 확인할 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;try:
    number = int(&quot;abc&quot;)
except ValueError as e:
    print(&quot;오류 내용:&quot;, e)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 오류 원인을 확인할 때 유용합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;else 사용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;else&lt;/code&gt;는 예외가 발생하지 않았을 때 실행됩니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;try:
    number = int(input(&quot;숫자를 입력하세요: &quot;))
except ValueError:
    print(&quot;숫자로 입력해야 합니다.&quot;)
else:
    print(&quot;입력한 숫자:&quot;, number)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예외가 발생하면 &lt;code&gt;except&lt;/code&gt;가 실행됩니다. 예외가 발생하지 않으면 &lt;code&gt;else&lt;/code&gt;가 실행됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성공했을 때 실행할 코드를 분리하고 싶다면 사용할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;finally 사용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;finally&lt;/code&gt;는 예외 발생 여부와 상관없이 항상 실행됩니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;try:
    file = open(&quot;sample.txt&quot;, &quot;r&quot;, encoding=&quot;utf-8&quot;)
    content = file.read()
except FileNotFoundError:
    print(&quot;파일을 찾을 수 없습니다.&quot;)
finally:
    print(&quot;작업을 종료합니다.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일을 찾든 찾지 못하든 &lt;code&gt;finally&lt;/code&gt; 아래 코드는 실행됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무에서는 파일 닫기, 연결 종료, 임시 작업 정리처럼 반드시 실행해야 하는 코드를 넣을 때 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 파일을 다룰 때는 보통 &lt;code&gt;with&lt;/code&gt; 문을 더 자주 사용합니다. 파일 입출력 글에서 따로 다루는 것이 좋습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;raise로 예외 직접 발생시키기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 경우 개발자가 직접 예외를 발생시킬 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;def check_age(age):
    if age &amp;lt; 0:
        raise ValueError(&quot;나이는 음수가 될 수 없습니다.&quot;)
    return age

print(check_age(20))
print(check_age(-1))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;age&lt;/code&gt;가 음수라면 &lt;code&gt;ValueError&lt;/code&gt;를 직접 발생시킵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 잘못된 값이 들어왔을 때 코드 흐름을 강제로 중단하고 싶을 때 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보 단계에서는 &amp;ldquo;조건에 맞지 않는 값이 들어오면 예외를 직접 만들 수도 있다&amp;rdquo; 정도로 이해하면 충분합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;자주 발생하는 예외 종류&lt;/h2&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;예외 이름&lt;/th&gt;
&lt;th&gt;발생 상황&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ValueError&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;값의 형태가 맞지 않을 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TypeError&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;자료형이 맞지 않을 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ZeroDivisionError&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0으로 나누려고 할 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IndexError&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;리스트 인덱스 범위를 벗어날 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;KeyError&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;딕셔너리에 없는 키를 사용할 때&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FileNotFoundError&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;파일을 찾을 수 없을 때&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예외 이름을 모두 외울 필요는 없습니다. 자주 만나는 오류부터 하나씩 익히면 됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;예외 처리를 너무 넓게 쓰지 않기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예외 처리는 편리하지만, 모든 코드를 감싸는 방식으로 쓰면 좋지 않습니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;try:
    # 아주 많은 코드
    pass
except Exception:
    print(&quot;오류 발생&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 코드는 어디에서 문제가 생겼는지 찾기 어렵습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예외 처리는 &amp;ldquo;오류가 발생할 가능성이 있는 부분&amp;rdquo;에만 좁게 적용하는 편이 좋습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 예외 처리는 실행 중 발생하는 오류를 안전하게 다루는 방법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 구조는 &lt;code&gt;try&lt;/code&gt;와 &lt;code&gt;except&lt;/code&gt;입니다. 예외가 발생할 수 있는 코드는 &lt;code&gt;try&lt;/code&gt;에 넣고, 오류가 발생했을 때 실행할 코드는 &lt;code&gt;except&lt;/code&gt;에 넣습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가능하면 구체적인 예외 이름을 사용하고, 예외 처리 범위는 너무 넓게 잡지 않는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 &lt;code&gt;ValueError&lt;/code&gt;, &lt;code&gt;ZeroDivisionError&lt;/code&gt;, &lt;code&gt;FileNotFoundError&lt;/code&gt;처럼 자주 만나는 예외부터 익히면 충분합니다.&lt;/p&gt;</description>
      <category>개발/python</category>
      <category>Python</category>
      <category>try except</category>
      <category>예외처리</category>
      <category>코딩입문</category>
      <category>파이썬</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/29</guid>
      <comments>https://notebase.tistory.com/entry/python-exception-handling-guide#entry29comment</comments>
      <pubDate>Fri, 22 May 2026 08:32:29 +0900</pubDate>
    </item>
    <item>
      <title>파이썬 함수: 반복되는 코드를 줄이고 재사용하는 방법</title>
      <link>https://notebase.tistory.com/entry/python-functions-guide</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬&amp;nbsp;함수의&amp;nbsp;기본&amp;nbsp;구조부터&amp;nbsp;매개변수,&amp;nbsp;return,&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 함수가 조금 낯설 수 있습니다. 하지만 실제로는 &amp;ldquo;자주 쓰는 작업에 이름을 붙이는 것&amp;rdquo;에 가깝습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;함수가 필요한 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 인사 문장을 여러 번 출력한다고 생각해보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;print(&quot;안녕하세요&quot;)
print(&quot;안녕하세요&quot;)
print(&quot;안녕하세요&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정도는 괜찮아 보일 수 있습니다. 하지만 같은 작업이 10번, 100번 반복된다면 코드가 금방 지저분해집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴 때 함수를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;def say_hello():
    print(&quot;안녕하세요&quot;)

say_hello()
say_hello()
say_hello()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;def say_hello():&lt;/code&gt;는 &lt;code&gt;say_hello&lt;/code&gt;라는 함수를 만든다는 뜻입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 아래 들여쓰기 된 코드는 함수가 실행될 때 동작하는 내용입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막의 &lt;code&gt;say_hello()&lt;/code&gt;는 만든 함수를 호출하는 코드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수는 만들어두기만 해서는 실행되지 않습니다. 반드시 함수 이름 뒤에 괄호를 붙여 호출해야 실행됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;함수의 기본 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 함수의 기본 구조는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;def 함수이름():
    실행할 코드&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 하나 더 보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;def print_line():
    print(&quot;----------------&quot;)

print_line()
print(&quot;회원 정보&quot;)
print_line()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;----------------
회원 정보
----------------&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;print_line()&lt;/code&gt; 함수는 구분선을 출력하는 역할만 합니다. 이렇게 간단한 함수라도 코드의 의미가 명확해집니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;매개변수가 있는 함수&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수는 외부에서 값을 받아 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 함수가 받는 값을 &lt;code&gt;매개변수&lt;/code&gt;라고 합니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;def say_hello(name):
    print(name + &quot;님, 안녕하세요&quot;)

say_hello(&quot;민수&quot;)
say_hello(&quot;지영&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;민수님, 안녕하세요
지영님, 안녕하세요&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;name&lt;/code&gt;이 매개변수입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수를 호출할 때 전달한 &lt;code&gt;&quot;민수&quot;&lt;/code&gt;, &lt;code&gt;&quot;지영&quot;&lt;/code&gt; 값이 함수 안의 &lt;code&gt;name&lt;/code&gt;에 들어갑니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;매개변수를 여러 개 받는 함수&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매개변수는 하나만 받을 수 있는 것이 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 개의 값을 받을 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;def add(a, b):
    print(a + b)

add(3, 5)
add(10, 20)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;8
30&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;a&lt;/code&gt;와 &lt;code&gt;b&lt;/code&gt;는 함수 안에서만 사용하는 이름입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수를 호출할 때 &lt;code&gt;add(3, 5)&lt;/code&gt;라고 쓰면 &lt;code&gt;a&lt;/code&gt;에는 3, &lt;code&gt;b&lt;/code&gt;에는 5가 들어갑니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;return으로 결과 돌려주기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지는 함수 안에서 바로 &lt;code&gt;print()&lt;/code&gt;를 사용했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제 코드에서는 함수가 계산한 결과를 다시 돌려줘야 할 때가 많습니다. 이때 &lt;code&gt;return&lt;/code&gt;을 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;livecodeserver&quot;&gt;&lt;code&gt;def add(a, b):
    return a + b

result = add(3, 5)
print(result)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;8&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;return a + b&lt;/code&gt;는 &lt;code&gt;a + b&lt;/code&gt;의 결과를 함수 밖으로 돌려준다는 뜻입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 호출 결과를 &lt;code&gt;result&lt;/code&gt; 변수에 저장한 뒤 출력할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;print와 return의 차이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보자가 자주 헷갈리는 부분이 &lt;code&gt;print&lt;/code&gt;와 &lt;code&gt;return&lt;/code&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;print()&lt;/code&gt;는 화면에 보여주는 역할입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;return&lt;/code&gt;은 함수의 결과를 밖으로 보내는 역할입니다.&lt;/p&gt;
&lt;pre class=&quot;sas&quot;&gt;&lt;code&gt;def add_print(a, b):
    print(a + b)

x = add_print(2, 3)
print(x)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;5
None&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 안에서 &lt;code&gt;print(a + b)&lt;/code&gt;로 5는 출력됐습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 &lt;code&gt;return&lt;/code&gt;을 하지 않았기 때문에 함수의 결과값은 &lt;code&gt;None&lt;/code&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 &lt;code&gt;return&lt;/code&gt;을 사용하면 결과를 변수에 저장해서 다시 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;sas&quot;&gt;&lt;code&gt;def add_return(a, b):
    return a + b

x = add_return(2, 3)
print(x * 10)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;50&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계산 결과를 다시 사용해야 한다면 &lt;code&gt;return&lt;/code&gt;을 쓰는 편이 좋습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기본값 매개변수&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수의 매개변수에는 기본값을 지정할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;def greet(name=&quot;사용자&quot;):
    print(name + &quot;님, 안녕하세요&quot;)

greet(&quot;민수&quot;)
greet()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;민수님, 안녕하세요
사용자님, 안녕하세요&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;greet()&lt;/code&gt;처럼 값을 전달하지 않으면 기본값인 &lt;code&gt;&quot;사용자&quot;&lt;/code&gt;가 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, 기본값이 있는 매개변수는 보통 기본값이 없는 매개변수 뒤에 둡니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;def introduce(name, age=20):
    print(name, age)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 형태는 자연스럽습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;키워드 인자&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수를 호출할 때 매개변수 이름을 직접 지정할 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;def introduce(name, age):
    print(&quot;이름:&quot;, name)
    print(&quot;나이:&quot;, age)

introduce(name=&quot;민수&quot;, age=25)
introduce(age=30, name=&quot;지영&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 매개변수 이름을 지정해서 전달하는 값을 &lt;code&gt;키워드 인자&lt;/code&gt;라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키워드 인자를 사용하면 순서가 바뀌어도 어떤 값이 어디에 들어가는지 명확합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;가변 인자 args&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몇 개의 값이 들어올지 모를 때는 &lt;code&gt;*args&lt;/code&gt;를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;def add_all(*args):
    total = 0
    for number in args:
        total += number
    return total

print(add_all(1, 2, 3))
print(add_all(10, 20, 30, 40))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;6
100&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;*args&lt;/code&gt;는 여러 값을 튜플 형태로 받습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수에 전달할 값의 개수가 정해져 있지 않을 때 유용합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;함수 안의 변수와 밖의 변수&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 안에서 만든 변수는 기본적으로 함수 안에서만 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;def test():
    message = &quot;안녕하세요&quot;
    print(message)

test()
print(message)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 오류가 납니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;message&lt;/code&gt;는 함수 안에서 만든 변수이기 때문에 함수 밖에서는 사용할 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 범위를 &lt;code&gt;스코프&lt;/code&gt;라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보 단계에서는 &amp;ldquo;함수 안에서 만든 변수는 함수 안에서만 쓴다&amp;rdquo;고 이해하면 충분합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;함수 이름을 지을 때 주의할 점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 이름은 코드의 의미를 설명할 수 있어야 합니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;def a():
    pass&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 이름은 좋지 않습니다. 무엇을 하는 함수인지 알기 어렵기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래처럼 동작이 드러나는 이름이 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;def calculate_total_price():
    pass

def print_user_info():
    pass

def get_average_score():
    pass&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서는 보통 소문자와 밑줄을 사용해 함수 이름을 짓습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;자주 하는 실수&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 함수를 만들고 호출하지 않음&lt;/h3&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;def hello():
    print(&quot;안녕하세요&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 함수를 만들기만 한 상태입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행하려면 아래 코드가 필요합니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;hello()&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 들여쓰기를 잘못함&lt;/h3&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;def hello():
print(&quot;안녕하세요&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬은 들여쓰기가 문법의 일부입니다. 함수 안의 코드는 반드시 들여쓰기해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;def hello():
    print(&quot;안녕하세요&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. return과 print를 혼동함&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 결과를 다시 사용해야 한다면 &lt;code&gt;return&lt;/code&gt;을 사용해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 화면에 보여주기만 한다면 &lt;code&gt;print()&lt;/code&gt;를 사용할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 함수는 코드를 재사용하기 위한 기본 문법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;def&lt;/code&gt;로 함수를 만들고, 함수 이름 뒤에 괄호를 붙여 호출합니다. 외부에서 값을 받을 때는 매개변수를 사용하고, 결과를 돌려줄 때는 &lt;code&gt;return&lt;/code&gt;을 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 작은 함수부터 만들어보는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &amp;ldquo;두 수를 더하는 함수&amp;rdquo;, &amp;ldquo;이름을 받아 인사하는 함수&amp;rdquo;, &amp;ldquo;점수 평균을 구하는 함수&amp;rdquo;처럼 단순한 함수부터 연습하면 함수의 구조가 자연스럽게 익숙해집니다.&lt;/p&gt;</description>
      <category>개발/python</category>
      <category>Python</category>
      <category>코딩입문</category>
      <category>파이썬</category>
      <category>파이썬기초</category>
      <category>함수</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/28</guid>
      <comments>https://notebase.tistory.com/entry/python-functions-guide#entry28comment</comments>
      <pubDate>Fri, 22 May 2026 08:18:20 +0900</pubDate>
    </item>
    <item>
      <title>파이썬 반복문 for while 사용법 쉽게 이해하기</title>
      <link>https://notebase.tistory.com/entry/python-for-while-loop-guide</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;if 조건문으로 흐름을 나눴다면, 이제 파이썬 반복문으로 같은 작업을 여러 번 실행하는 방법을 익힐 차례입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복할 대상이 정해져 있으면 &lt;code&gt;for문&lt;/code&gt;, 조건이 참인 동안 계속 실행해야 하면 &lt;code&gt;while문&lt;/code&gt;이 더 잘 맞습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 두 문법이 비슷해 보여도, 언제 어떤 문법을 써야 하는지 구분해두면 코드가 훨씬 읽기 쉬워집니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;반복문이 필요한 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 코드를 여러 번 써야 할 때 반복문이 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 같은 문장을 세 번 출력한다고 해서 아래처럼 직접 세 줄을 쓰는 방식은 금방 불편해집니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;print(&quot;안녕하세요&quot;)
print(&quot;안녕하세요&quot;)
print(&quot;안녕하세요&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 작업은 반복문으로 더 간단하게 표현할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;for i in range(3):
    print(&quot;안녕하세요&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과:&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;안녕하세요
안녕하세요
안녕하세요&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복문을 사용하면 코드 길이가 줄고, 같은 작업을 여러 번 처리할 때 실수도 줄일 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;for문 기본 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;for문&lt;/code&gt;은 반복할 대상에서 값을 하나씩 꺼내며 실행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 형태는 아래와 같습니다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;for 변수 in 반복할대상:
    실행할 코드&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 예제를 보면 더 이해하기 쉽습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;for number in [1, 2, 3]:
    print(number)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1
2
3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 리스트 안의 값을 하나씩 꺼내서 &lt;code&gt;number&lt;/code&gt;에 넣고 출력합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;리스트와 함께 사용하는 for문&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트는 &lt;code&gt;for문&lt;/code&gt;과 가장 자주 함께 쓰는 자료형입니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;fruits = [&quot;사과&quot;, &quot;바나나&quot;, &quot;딸기&quot;]

for fruit in fruits:
    print(fruit)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과:&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;사과
바나나
딸기&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트 안의 값을 처음부터 끝까지 한 번씩 확인하고 싶을 때 &lt;code&gt;for문&lt;/code&gt;이 자연스럽습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문자열과 함께 사용하는 for문&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열도 글자들이 순서대로 들어 있기 때문에 &lt;code&gt;for문&lt;/code&gt;으로 한 글자씩 꺼낼 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;for letter in &quot;Python&quot;:
    print(letter)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과:&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;P
y
t
h
o
n&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열을 한 글자씩 검사하거나, 특정 글자가 있는지 확인하는 기초 연습에 자주 사용합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;range() 사용법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복 횟수가 정해져 있을 때는 &lt;code&gt;range()&lt;/code&gt;를 많이 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;for number in range(5):
    print(number)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;0
1
2
3
4&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;range(5)&lt;/code&gt;는 &lt;code&gt;0&lt;/code&gt;부터 시작해서 &lt;code&gt;5&lt;/code&gt; 바로 전까지 숫자를 만듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &lt;code&gt;5&lt;/code&gt;번 반복하고 싶을 때 자주 사용하는 형태입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;range(start, stop)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 숫자를 직접 정하고 싶다면 &lt;code&gt;range(start, stop)&lt;/code&gt; 형태로 쓸 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;for number in range(1, 5):
    print(number)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1
2
3
4&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점은 &lt;code&gt;stop&lt;/code&gt; 값인 &lt;code&gt;5&lt;/code&gt;는 포함되지 않는다는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 &lt;code&gt;1&lt;/code&gt;부터 &lt;code&gt;5&lt;/code&gt;까지라고 생각하기 쉬운데, 실제로는 &lt;code&gt;1&lt;/code&gt;부터 &lt;code&gt;4&lt;/code&gt;까지입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;range(start, stop, step)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 번에 얼마나 증가할지도 정할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;for number in range(1, 10, 2):
    print(number)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1
3
5
7
9&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;step&lt;/code&gt;은 숫자가 얼마나씩 건너뛸지를 뜻합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제는 &lt;code&gt;1&lt;/code&gt;부터 시작해서 &lt;code&gt;2&lt;/code&gt;씩 증가하며 &lt;code&gt;10&lt;/code&gt; 전까지 반복합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;while문 기본 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;while문&lt;/code&gt;은 조건식이 참인 동안 계속 실행됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 형태는 아래와 같습니다.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;while 조건식:
    실행할 코드&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제를 보면 &lt;code&gt;for문&lt;/code&gt;과 차이가 더 분명해집니다.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;count = 1

while count &amp;lt;= 3:
    print(count)
    count += 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1
2
3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 &lt;code&gt;count &amp;lt;= 3&lt;/code&gt;이 참일 동안 반복하고, 반복할 때마다 &lt;code&gt;count&lt;/code&gt; 값을 1씩 늘립니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;while문에서 조건식이 중요한 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;while문&lt;/code&gt;은 반복 횟수보다 &lt;b&gt;조건이 언제 거짓이 되는지&lt;/b&gt;가 더 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서 &lt;code&gt;count += 1&lt;/code&gt;이 없다면 &lt;code&gt;count&lt;/code&gt;는 계속 &lt;code&gt;1&lt;/code&gt;인 상태로 남습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 &lt;code&gt;count &amp;lt;= 3&lt;/code&gt;은 계속 참이 되고, 반복문은 끝나지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &lt;code&gt;while문&lt;/code&gt;에서는 조건식뿐 아니라 조건에 영향을 주는 값이 반복 안에서 바뀌는지도 함께 봐야 합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;무한 반복 주의&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;while문&lt;/code&gt;을 처음 배울 때 가장 자주 하는 실수 중 하나가 무한 반복입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드는 반복이 끝나지 않습니다.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;count = 1

while count &amp;lt;= 3:
    print(count)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이유는 &lt;code&gt;count&lt;/code&gt; 값이 변하지 않기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;while문&lt;/code&gt;을 쓸 때는 &quot;이 반복이 언제 끝나는가?&quot;를 먼저 확인하는 습관이 필요합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;break 사용법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;break&lt;/code&gt;는 반복문을 즉시 끝낼 때 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;for number in [1, 2, 3, 4, 5]:
    if number == 4:
        break
    print(number)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1
2
3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;number&lt;/code&gt;가 &lt;code&gt;4&lt;/code&gt;가 되는 순간 반복문이 바로 종료됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;for문&lt;/code&gt;과 &lt;code&gt;while문&lt;/code&gt; 모두에서 사용할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;continue 사용법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;continue&lt;/code&gt;는 반복문을 끝내는 것이 아니라, &lt;b&gt;이번 차례만 건너뛰고 다음 반복으로 넘어갈 때&lt;/b&gt; 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;fruits = [&quot;사과&quot;, &quot;바나나&quot;, &quot;딸기&quot;]

for fruit in fruits:
    if fruit == &quot;바나나&quot;:
        continue
    print(fruit)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과:&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;사과
딸기&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;바나나&lt;/code&gt;일 때는 &lt;code&gt;print()&lt;/code&gt;를 실행하지 않고 다음 값으로 넘어간 것입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;for문과 while문의 차이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 반복문은 모두 같은 작업을 여러 번 실행할 때 쓰지만 기준이 다릅니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;for문&lt;/code&gt;은 반복할 대상이나 횟수가 비교적 분명할 때 사용합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;while문&lt;/code&gt;은 조건이 참인 동안 계속 실행해야 할 때 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;짧게 정리하면 아래처럼 볼 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;for문: 리스트, 문자열, range()처럼 반복할 대상이 보일 때
while문: 조건이 언제 거짓이 되는지가 더 중요할 때&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;for문을 쓰기 좋은 경우&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래처럼 반복할 대상이 이미 정해져 있으면 &lt;code&gt;for문&lt;/code&gt;이 더 간단합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리스트 값을 하나씩 확인할 때&lt;/li&gt;
&lt;li&gt;문자열을 한 글자씩 볼 때&lt;/li&gt;
&lt;li&gt;&lt;code&gt;range()&lt;/code&gt;로 정해진 횟수만큼 반복할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 학생 점수를 순서대로 출력하는 작업은 &lt;code&gt;for문&lt;/code&gt;이 잘 맞습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;while문을 쓰기 좋은 경우&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복 횟수를 미리 딱 잘라 말하기보다, 어떤 조건이 바뀔 때까지 계속 돌아야 하면 &lt;code&gt;while문&lt;/code&gt;이 편합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;숫자가 특정 값이 될 때까지 줄이거나 늘릴 때&lt;/li&gt;
&lt;li&gt;어떤 상태가 바뀔 때까지 반복할 때&lt;/li&gt;
&lt;li&gt;반복 종료 시점이 조건에 따라 달라질 때&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 카운트다운처럼 값이 계속 바뀌면서 조건이 달라지는 작업은 &lt;code&gt;while문&lt;/code&gt;으로 자주 표현합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;자주 발생하는 오류&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;숫자를 바로 for문에 넣는 경우&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드는 초보자가 자주 헷갈리는 형태입니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;for i in 3:
    print(i)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자 &lt;code&gt;3&lt;/code&gt; 자체는 반복할 대상이 아니기 때문에 오류가 납니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복 횟수를 정하고 싶다면 &lt;code&gt;range(3)&lt;/code&gt;처럼 써야 합니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;for i in range(3):
    print(i)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;0
1
2&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;range()의 끝값도 포함된다고 생각하는 경우&lt;/h3&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;for number in range(1, 5):
    print(number)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1
2
3
4&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;5&lt;/code&gt;가 출력되지 않는다는 점을 꼭 기억해야 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;while문 안에서 값을 바꾸지 않는 경우&lt;/h3&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;count = 1

while count &amp;lt;= 3:
    print(count)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 &lt;code&gt;count&lt;/code&gt;가 계속 &lt;code&gt;1&lt;/code&gt;이라서 반복이 끝나지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;while문&lt;/code&gt;에서는 조건을 바꿀 코드가 있는지 항상 같이 확인해야 합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;초보자용 예제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 &lt;code&gt;for문&lt;/code&gt; 예제입니다. 리스트 안에서 60점 이상인 점수만 출력합니다.&lt;/p&gt;
&lt;pre class=&quot;mipsasm&quot;&gt;&lt;code&gt;scores = [75, 52, 88, 60]

for score in scores:
    if score &amp;gt;= 60:
        print(score, &quot;점 통과&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과:&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;75 점 통과
88 점 통과
60 점 통과&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 &lt;code&gt;while문&lt;/code&gt; 예제입니다. 숫자를 하나씩 줄이며 카운트다운합니다.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;count = 3

while count &amp;gt; 0:
    print(count)
    count -= 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;3
2
1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 두 예제만 비교해봐도 &lt;code&gt;for문&lt;/code&gt;은 정해진 값들을 순서대로 볼 때, &lt;code&gt;while문&lt;/code&gt;은 조건이 바뀌는 흐름을 만들 때 더 어울린다는 점이 보입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 반복문은 같은 작업을 여러 번 실행할 때 사용하는 기본 문법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 배우는 단계에서는 아래 흐름만 분명히 이해해도 충분합니다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;for문: 반복할 대상이 정해져 있을 때
while문: 조건이 참인 동안 반복할 때
break: 반복을 바로 끝낼 때
continue: 이번 차례만 건너뛸 때&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 &lt;code&gt;range()&lt;/code&gt;와 조건식의 동작까지 익히면, 리스트와 문자열을 다루는 반복문부터 간단한 조건 반복까지 자연스럽게 연결할 수 있습니다.&lt;/p&gt;</description>
      <category>개발/python</category>
      <category>Python</category>
      <category>파이썬</category>
      <category>파이썬 for문</category>
      <category>파이썬 기초</category>
      <category>파이썬 반복문</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/27</guid>
      <comments>https://notebase.tistory.com/entry/python-for-while-loop-guide#entry27comment</comments>
      <pubDate>Thu, 21 May 2026 19:03:38 +0900</pubDate>
    </item>
    <item>
      <title>파이썬 조건문 if 사용법 쉽게 이해하기</title>
      <link>https://notebase.tistory.com/entry/python-if-statement-guide</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리와 집합으로 값을 정리했다면, 이제 파이썬 조건문 if로 상황에 따라 다른 코드를 실행하는 방법을 배울 차례입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값이 같을 때만 실행하거나, 특정 조건에서만 다른 결과를 보여주고 싶을 때 if를 사용합니다.&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;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램은 항상 같은 일만 반복하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나이에 따라 다른 안내를 보여주거나, 점수에 따라 다른 결과를 출력해야 할 때가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래처럼 점수가 60점 이상일 때만 합격이라고 표시할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;score = 75

if score &amp;gt;= 60:
    print(&quot;합격&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과:&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;합격&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건문이 없다면 상황에 따라 다른 동작을 만들기 어렵습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;if 기본 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;if 조건문은 아래 형태로 작성합니다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;if 조건식:
    실행할 코드&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점은 두 가지입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;if&lt;/code&gt; 뒤에는 조건식이 옵니다.&lt;/li&gt;
&lt;li&gt;조건식 뒤에는 반드시 &lt;code&gt;:&lt;/code&gt;가 붙습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 예제를 보면 더 이해하기 쉽습니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;age = 20

if age &amp;gt;= 19:
    print(&quot;성인입니다&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과:&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;성인입니다&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건식이 참이면 들여쓰기된 코드가 실행됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;들여쓰기의 중요성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬은 들여쓰기로 코드의 범위를 구분합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, if 아래에서 어디까지 실행할지를 들여쓰기로 판단합니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;age = 20

if age &amp;gt;= 19:
    print(&quot;성인입니다&quot;)
    print(&quot;입장 가능합니다&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과:&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;성인입니다
입장 가능합니다&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;들여쓰기가 맞지 않으면 오류가 나거나, 생각과 다른 위치에서 코드가 실행될 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;age = 20

if age &amp;gt;= 19:
print(&quot;성인입니다&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 들여쓰기가 없어서 오류가 납니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보 단계에서는 공백 4칸 들여쓰기를 습관처럼 쓰는 것이 가장 편합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;비교 연산자&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;if 조건문에서는 비교 연산자를 자주 사용합니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;연산자&lt;/th&gt;
&lt;th&gt;의미&lt;/th&gt;
&lt;th&gt;예시&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;==&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;같다&lt;/td&gt;
&lt;td&gt;&lt;code&gt;age == 20&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;!=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;같지 않다&lt;/td&gt;
&lt;td&gt;&lt;code&gt;age != 20&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;크다&lt;/td&gt;
&lt;td&gt;&lt;code&gt;score &amp;gt; 80&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;작다&lt;/td&gt;
&lt;td&gt;&lt;code&gt;score &amp;lt; 80&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;크거나 같다&lt;/td&gt;
&lt;td&gt;&lt;code&gt;score &amp;gt;= 60&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;작거나 같다&lt;/td&gt;
&lt;td&gt;&lt;code&gt;score &amp;lt;= 100&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래처럼 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;number = 10

if number == 10:
    print(&quot;10입니다&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;10입니다&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;=&lt;/code&gt;는 값을 저장할 때 사용하고, &lt;code&gt;==&lt;/code&gt;는 같은지 비교할 때 사용한다는 점을 구분해야 합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;if else&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건이 참일 때와 거짓일 때를 나누고 싶다면 &lt;code&gt;else&lt;/code&gt;를 함께 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;score = 52

if score &amp;gt;= 60:
    print(&quot;합격&quot;)
else:
    print(&quot;불합격&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과:&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;불합격&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;if&lt;/code&gt;는 조건이 맞을 때 실행하고, &lt;code&gt;else&lt;/code&gt;는 그 외의 경우를 처리합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;if elif else&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건이 두 개보다 많다면 &lt;code&gt;elif&lt;/code&gt;를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;score = 85

if score &amp;gt;= 90:
    print(&quot;A&quot;)
elif score &amp;gt;= 80:
    print(&quot;B&quot;)
elif score &amp;gt;= 70:
    print(&quot;C&quot;)
else:
    print(&quot;D&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;B&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 위에서부터 순서대로 검사합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 조건이 참이 되면 그 아래 조건은 더 보지 않습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;여러 조건을 연결하는 and, or, not&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건을 하나만 쓰는 경우도 많지만, 두 개 이상을 함께 확인할 때도 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;and&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;and&lt;/code&gt;는 두 조건이 모두 참일 때만 참입니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;age = 20
has_ticket = True

if age &amp;gt;= 19 and has_ticket:
    print(&quot;입장 가능합니다&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과:&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;입장 가능합니다&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;or&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;or&lt;/code&gt;는 둘 중 하나만 참이어도 참입니다.&lt;/p&gt;
&lt;pre class=&quot;processing&quot;&gt;&lt;code&gt;day = &quot;토요일&quot;

if day == &quot;토요일&quot; or day == &quot;일요일&quot;:
    print(&quot;주말입니다&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과:&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;주말입니다&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;not&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;not&lt;/code&gt;은 참과 거짓을 뒤집습니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;is_closed = False

if not is_closed:
    print(&quot;영업 중입니다&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과:&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;영업 중입니다&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;in을 활용한 조건문&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 글에서 배운 리스트, 딕셔너리, 집합에서도 조건문을 자주 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;in&lt;/code&gt;은 어떤 값이 들어 있는지 확인할 때 편리합니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;tags = {&quot;python&quot;, &quot;basic&quot;, &quot;if&quot;}

if &quot;python&quot; in tags:
    print(&quot;파이썬 태그가 있습니다&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과:&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;파이썬 태그가 있습니다&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리에서는 key가 있는지 확인할 때도 쓸 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;nsis&quot;&gt;&lt;code&gt;user = {&quot;name&quot;: &quot;철수&quot;, &quot;age&quot;: 20}

if &quot;name&quot; in user:
    print(user[&quot;name&quot;])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과:&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;철수&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;bool 값과 조건문&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;True&lt;/code&gt;, &lt;code&gt;False&lt;/code&gt; 같은 bool 값은 조건문과 아주 잘 맞습니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;is_logged_in = True

if is_logged_in:
    print(&quot;로그인 상태입니다&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과:&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;로그인 상태입니다&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;굳이 &lt;code&gt;if is_logged_in == True:&lt;/code&gt;처럼 길게 쓰지 않아도 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통은 아래처럼 짧게 씁니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;if is_logged_in:
    print(&quot;로그인 상태입니다&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 False인지 보고 싶다면 &lt;code&gt;not&lt;/code&gt;을 함께 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;is_logged_in = False

if not is_logged_in:
    print(&quot;로그인이 필요합니다&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과:&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;로그인이 필요합니다&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;자주 발생하는 오류&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. &lt;code&gt;=&lt;/code&gt;와 &lt;code&gt;==&lt;/code&gt;를 헷갈리는 경우&lt;/h3&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;age = 20

if age = 20:
    print(&quot;같습니다&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 오류가 납니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건 비교는 &lt;code&gt;==&lt;/code&gt;를 써야 합니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;if age == 20:
    print(&quot;같습니다&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 콜론 &lt;code&gt;:&lt;/code&gt;을 빼먹는 경우&lt;/h3&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;score = 90

if score &amp;gt;= 80
    print(&quot;통과&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;if&lt;/code&gt; 조건식 뒤에는 반드시 &lt;code&gt;:&lt;/code&gt;가 필요합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 들여쓰기를 하지 않는 경우&lt;/h3&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;score = 90

if score &amp;gt;= 80:
print(&quot;통과&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드도 오류가 납니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;if 아래의 실행문은 들여써야 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 순서를 잘못 써서 결과가 달라지는 경우&lt;/h3&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;score = 95

if score &amp;gt;= 60:
    print(&quot;합격&quot;)
elif score &amp;gt;= 90:
    print(&quot;우수&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과:&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;합격&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;95&lt;/code&gt;는 90 이상이기도 하지만, 위에서 먼저 &lt;code&gt;score &amp;gt;= 60&lt;/code&gt;이 참이 되었기 때문에 아래 &lt;code&gt;elif&lt;/code&gt;까지 가지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건이 더 엄격한 쪽을 위에 두는 습관이 필요합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;초보자용 예제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 &lt;code&gt;in&lt;/code&gt;, &lt;code&gt;and&lt;/code&gt;, bool 값을 함께 사용하는 간단한 예제입니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;cart = [&quot;우유&quot;, &quot;빵&quot;, &quot;사과&quot;]
is_member = True
total = 12000

if &quot;우유&quot; in cart:
    print(&quot;우유가 장바구니에 있습니다&quot;)

if is_member and total &amp;gt;= 10000:
    print(&quot;할인 대상입니다&quot;)
else:
    print(&quot;할인 대상이 아닙니다&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과:&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;우유가 장바구니에 있습니다
할인 대상입니다&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정도 예제를 직접 여러 번 바꿔보면 조건문이 훨씬 빨리 익숙해집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 &lt;code&gt;total&lt;/code&gt; 값을 바꾸거나, &lt;code&gt;is_member&lt;/code&gt;를 &lt;code&gt;False&lt;/code&gt;로 바꿔서 결과가 어떻게 달라지는지 확인해보면 좋습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 조건문 if는 조건에 따라 코드를 다르게 실행할 때 사용하는 기본 문법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보 단계에서는 아래 흐름만 먼저 확실히 익히면 충분합니다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;if: 조건이 참일 때 실행
if else: 참/거짓 두 경우 나누기
if elif else: 여러 경우 나누기
and, or, not: 조건 연결하기
in: 포함 여부 확인하기&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 들여쓰기와 &lt;code&gt;:&lt;/code&gt;는 문법의 일부이기 때문에 꼭 함께 익혀야 합니다.&lt;/p&gt;</description>
      <category>개발/python</category>
      <category>Python</category>
      <category>파이썬</category>
      <category>파이썬 if</category>
      <category>파이썬 기초</category>
      <category>파이썬 조건문</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/26</guid>
      <comments>https://notebase.tistory.com/entry/python-if-statement-guide#entry26comment</comments>
      <pubDate>Thu, 21 May 2026 18:43:56 +0900</pubDate>
    </item>
    <item>
      <title>파이썬 딕셔너리와 집합 set 쉽게 이해하기</title>
      <link>https://notebase.tistory.com/entry/python-dictionary-set-guide</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 딕셔너리와 집합 set은 여러 값을 다룰 때 사용하는 자료형입니다. 딕셔너리는 이름과 값을 짝으로 저장하고, 집합은 중복 없는 값을 모아둘 때 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 글에서 리스트와 튜플을 다뤘습니다. 리스트와 튜플은 여러 값을 순서대로 저장하는 자료형입니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;fruits = [&quot;사과&quot;, &quot;바나나&quot;, &quot;딸기&quot;]
print(fruits[0])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;사과&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 모든 데이터를 위치로만 다루면 불편한 경우가 있습니다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;user = [&quot;철수&quot;, 20, &quot;서울&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 저장하면 &lt;code&gt;user[0]&lt;/code&gt;이 이름이고, &lt;code&gt;user[1]&lt;/code&gt;이 나이이며, &lt;code&gt;user[2]&lt;/code&gt;가 지역이라는 사실을 따로 기억해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터가 많아질수록 헷갈리기 쉽습니다. 이럴 때 사용하는 자료형이 &lt;b&gt;딕셔너리(dictionary)&lt;/b&gt; 입니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;user = {
    &quot;name&quot;: &quot;철수&quot;,
    &quot;age&quot;: 20,
    &quot;city&quot;: &quot;서울&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리를 사용하면 값에 이름표를 붙여서 관리할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;딕셔너리는 key와 value를 저장하는 자료형이다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리는 &lt;b&gt;key&lt;/b&gt;와 &lt;b&gt;value&lt;/b&gt;를 한 쌍으로 저장합니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;user = {
    &quot;name&quot;: &quot;철수&quot;,
    &quot;age&quot;: 20,
    &quot;city&quot;: &quot;서울&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 &lt;code&gt;&quot;name&quot;&lt;/code&gt;, &lt;code&gt;&quot;age&quot;&lt;/code&gt;, &lt;code&gt;&quot;city&quot;&lt;/code&gt;는 key입니다.&lt;br /&gt;&lt;code&gt;&quot;철수&quot;&lt;/code&gt;, &lt;code&gt;20&lt;/code&gt;, &lt;code&gt;&quot;서울&quot;&lt;/code&gt;은 value입니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;key&lt;/th&gt;
&lt;th&gt;value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;name&lt;/td&gt;
&lt;td&gt;철수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;age&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;city&lt;/td&gt;
&lt;td&gt;서울&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리는 리스트처럼 위치 번호로 값을 꺼내기보다, key를 사용해서 값을 꺼냅니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;print(user[&quot;name&quot;])
print(user[&quot;age&quot;])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;철수
20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 딕셔너리는 이렇게 이해하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;q&quot;&gt;&lt;code&gt;key를 넣으면 value가 나온다&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;파이썬 딕셔너리 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리는 중괄호 &lt;code&gt;{}&lt;/code&gt;를 사용해서 만듭니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;student = {
    &quot;name&quot;: &quot;영희&quot;,
    &quot;score&quot;: 95
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리 안의 key와 value는 콜론 &lt;code&gt;:&lt;/code&gt;으로 연결합니다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;name&quot;: &quot;영희&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 쌍을 넣을 때는 쉼표 &lt;code&gt;,&lt;/code&gt;로 구분합니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;student = {
    &quot;name&quot;: &quot;영희&quot;,
    &quot;score&quot;: 95,
    &quot;grade&quot;: &quot;A&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빈 딕셔너리도 만들 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;user = {}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빈 딕셔너리는 나중에 값을 하나씩 추가할 때 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;nsis&quot;&gt;&lt;code&gt;user = {}

user[&quot;name&quot;] = &quot;철수&quot;
user[&quot;age&quot;] = 20

print(user)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;{'name': '철수', 'age': 20}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;딕셔너리에서 값 꺼내기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리에서 값을 꺼낼 때는 key를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;user = {
    &quot;name&quot;: &quot;철수&quot;,
    &quot;age&quot;: 20,
    &quot;city&quot;: &quot;서울&quot;
}

print(user[&quot;name&quot;])
print(user[&quot;city&quot;])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;철수
서울&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트는 인덱스로 값을 꺼냅니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;fruits = [&quot;사과&quot;, &quot;바나나&quot;, &quot;딸기&quot;]
print(fruits[0])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리는 key로 값을 꺼냅니다.&lt;/p&gt;
&lt;pre class=&quot;nsis&quot;&gt;&lt;code&gt;user = {
    &quot;name&quot;: &quot;철수&quot;
}

print(user[&quot;name&quot;])&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;get()으로 값 꺼내기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리에서 값을 꺼낼 때는 &lt;code&gt;get()&lt;/code&gt;을 사용할 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;user = {
    &quot;name&quot;: &quot;철수&quot;,
    &quot;age&quot;: 20
}

print(user.get(&quot;name&quot;))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;철수&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대괄호와 &lt;code&gt;get()&lt;/code&gt;은 없는 key를 사용할 때 차이가 있습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;print(user[&quot;city&quot;])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 오류가 발생합니다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;KeyError: 'city'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 &lt;code&gt;get()&lt;/code&gt;은 없는 key를 사용해도 오류가 나지 않고 &lt;code&gt;None&lt;/code&gt;을 반환합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;print(user.get(&quot;city&quot;))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본값을 직접 지정할 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;print(user.get(&quot;city&quot;, &quot;지역 정보 없음&quot;))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;지역 정보 없음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보 단계에서는 이렇게 기억하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;확실히 있는 key라면 []
없는 key일 수도 있다면 get()&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;딕셔너리에 값 추가하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리에 값을 추가하는 방법은 간단합니다.&lt;/p&gt;
&lt;pre class=&quot;nsis&quot;&gt;&lt;code&gt;user = {
    &quot;name&quot;: &quot;철수&quot;,
    &quot;age&quot;: 20
}

user[&quot;city&quot;] = &quot;서울&quot;

print(user)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;{'name': '철수', 'age': 20, 'city': '서울'}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 key를 사용해서 값을 넣으면 딕셔너리에 항목이 추가됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;딕셔너리 값 수정하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 있는 key에 새로운 값을 넣으면 기존 값이 수정됩니다.&lt;/p&gt;
&lt;pre class=&quot;nsis&quot;&gt;&lt;code&gt;user = {
    &quot;name&quot;: &quot;철수&quot;,
    &quot;age&quot;: 20
}

user[&quot;age&quot;] = 21

print(user)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;{'name': '철수', 'age': 21}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;user[&quot;city&quot;] = &quot;서울&quot;  # city가 없으면 추가
user[&quot;age&quot;] = 21      # age가 있으면 수정&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;딕셔너리 값 삭제하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리에서 값을 삭제할 때는 &lt;code&gt;del&lt;/code&gt;을 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;nsis&quot;&gt;&lt;code&gt;user = {
    &quot;name&quot;: &quot;철수&quot;,
    &quot;age&quot;: 20,
    &quot;city&quot;: &quot;서울&quot;
}

del user[&quot;city&quot;]

print(user)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;{'name': '철수', 'age': 20}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;pop()&lt;/code&gt;을 사용할 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;user = {
    &quot;name&quot;: &quot;철수&quot;,
    &quot;age&quot;: 20,
    &quot;city&quot;: &quot;서울&quot;
}

city = user.pop(&quot;city&quot;)

print(city)
print(user)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;서울
{'name': '철수', 'age': 20}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;pop()&lt;/code&gt;은 값을 삭제하면서 삭제한 값을 반환합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;딕셔너리 key, value 확인하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리의 key만 확인하고 싶을 때는 &lt;code&gt;keys()&lt;/code&gt;를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;nsis&quot;&gt;&lt;code&gt;user = {
    &quot;name&quot;: &quot;철수&quot;,
    &quot;age&quot;: 20,
    &quot;city&quot;: &quot;서울&quot;
}

print(user.keys())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;value만 확인하고 싶을 때는 &lt;code&gt;values()&lt;/code&gt;를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;print(user.values())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;key와 value를 함께 확인하고 싶을 때는 &lt;code&gt;items()&lt;/code&gt;를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;print(user.items())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;items()&lt;/code&gt;는 이후 반복문을 배울 때 자주 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;for key, value in user.items():
    print(key, value)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;name 철수
age 20
city 서울&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복문은 뒤에서 따로 다루므로, 지금은 &lt;code&gt;items()&lt;/code&gt;가 key와 value를 함께 가져올 때 쓰인다는 정도만 이해하면 됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;딕셔너리에 key가 있는지 확인하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리에 특정 key가 있는지 확인할 때는 &lt;code&gt;in&lt;/code&gt;을 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;user = {
    &quot;name&quot;: &quot;철수&quot;,
    &quot;age&quot;: 20
}

print(&quot;name&quot; in user)
print(&quot;city&quot; in user)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;True
False&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리에서 &lt;code&gt;in&lt;/code&gt;은 기본적으로 key를 기준으로 확인합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;value에 특정 값이 있는지 확인하고 싶다면 &lt;code&gt;values()&lt;/code&gt;를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;print(&quot;철수&quot; in user.values())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;True&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;딕셔너리를 사용할 때 좋은 경우&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리는 값에 이름을 붙여서 관리해야 할 때 유용합니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;member = {
    &quot;id&quot;: &quot;user01&quot;,
    &quot;name&quot;: &quot;철수&quot;,
    &quot;email&quot;: &quot;user01@example.com&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상품 정보도 딕셔너리로 표현하기 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;product = {
    &quot;name&quot;: &quot;노트북&quot;,
    &quot;price&quot;: 1200000,
    &quot;stock&quot;: 5
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정값을 저장할 때도 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;settings = {
    &quot;theme&quot;: &quot;dark&quot;,
    &quot;language&quot;: &quot;ko&quot;,
    &quot;notification&quot;: True
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트와 비교하면 차이가 더 분명해집니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;product = [&quot;노트북&quot;, 1200000, 5]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 쓰면 각 값이 무엇을 의미하는지 바로 알기 어렵습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 딕셔너리는 key가 의미를 설명해줍니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;product = {
    &quot;name&quot;: &quot;노트북&quot;,
    &quot;price&quot;: 1200000,
    &quot;stock&quot;: 5
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 여러 속성을 가진 하나의 대상을 표현할 때는 딕셔너리가 적합합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;집합 set은 무엇인가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;집합 set은 여러 값을 저장하는 자료형입니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;numbers = {1, 2, 3}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;집합은 중괄호 &lt;code&gt;{}&lt;/code&gt;를 사용합니다. 다만 딕셔너리와 다르게 key와 value 쌍이 없습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# 딕셔너리
user = {&quot;name&quot;: &quot;철수&quot;, &quot;age&quot;: 20}

# 집합
numbers = {1, 2, 3}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리는 콜론 &lt;code&gt;:&lt;/code&gt;이 있습니다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;&quot;name&quot;: &quot;철수&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;집합은 값만 나열합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;{1, 2, 3}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;집합은 중복을 허용하지 않는다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;집합 set의 가장 중요한 특징은 &lt;b&gt;중복을 허용하지 않는다는 것&lt;/b&gt;입니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;numbers = {1, 2, 2, 3, 3, 3}

print(numbers)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 비슷합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;{1, 2, 3}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중복된 값은 하나만 남습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 특징 때문에 집합은 중복 제거에 자주 사용됩니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;names = [&quot;철수&quot;, &quot;영희&quot;, &quot;철수&quot;, &quot;민수&quot;, &quot;영희&quot;]

unique_names = set(names)

print(unique_names)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 비슷합니다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;{'철수', '영희', '민수'}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의할 점은 집합은 리스트처럼 순서를 기준으로 사용하는 자료형이 아니라는 점입니다.&lt;br /&gt;출력 순서는 실행 환경에 따라 다르게 보일 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;빈 집합 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빈 집합을 만들 때는 주의해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;empty = {}

print(type(empty))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;&amp;lt;class 'dict'&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;{}&lt;/code&gt;는 빈 집합이 아니라 빈 딕셔너리입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빈 집합을 만들려면 &lt;code&gt;set()&lt;/code&gt;을 사용해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;empty_set = set()

print(type(empty_set))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;&amp;lt;class 'set'&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이렇게 기억하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;{}      &amp;rarr; 빈 딕셔너리
set()   &amp;rarr; 빈 집합&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;집합에 값 추가하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;집합에 값을 추가할 때는 &lt;code&gt;add()&lt;/code&gt;를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;numbers = {1, 2, 3}

numbers.add(4)

print(numbers)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 비슷합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;{1, 2, 3, 4}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 있는 값을 추가하면 중복으로 들어가지 않습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;numbers = {1, 2, 3}

numbers.add(2)

print(numbers)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;{1, 2, 3}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;집합에서 값 삭제하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;집합에서 값을 삭제할 때는 &lt;code&gt;remove()&lt;/code&gt;를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;numbers = {1, 2, 3}

numbers.remove(2)

print(numbers)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;{1, 3}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 &lt;code&gt;remove()&lt;/code&gt;는 없는 값을 삭제하려고 하면 오류가 발생합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;numbers.remove(4)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;KeyError: 4&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류 없이 삭제하고 싶다면 &lt;code&gt;discard()&lt;/code&gt;를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;numbers = {1, 2, 3}

numbers.discard(4)

print(numbers)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;{1, 2, 3}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보 단계에서는 이렇게 구분하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;값이 확실히 있으면 remove()
없을 수도 있으면 discard()&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;집합 연산 이해하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;집합은 수학의 집합처럼 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;python_students = {&quot;철수&quot;, &quot;영희&quot;, &quot;민수&quot;}
java_students = {&quot;영희&quot;, &quot;지연&quot;, &quot;민수&quot;}&lt;/code&gt;&lt;/pre&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;pre class=&quot;isbl&quot;&gt;&lt;code&gt;result = python_students | java_students
print(result)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 비슷합니다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;{'철수', '영희', '민수', '지연'}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;union()&lt;/code&gt;을 사용할 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;result = python_students.union(java_students)&lt;/code&gt;&lt;/pre&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;pre class=&quot;isbl&quot;&gt;&lt;code&gt;result = python_students &amp;amp; java_students
print(result)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 비슷합니다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;{'영희', '민수'}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;intersection()&lt;/code&gt;을 사용할 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;result = python_students.intersection(java_students)&lt;/code&gt;&lt;/pre&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;pre class=&quot;isbl&quot;&gt;&lt;code&gt;result = python_students - java_students
print(result)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;{'철수'}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 쓰면 결과가 달라집니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;result = java_students - python_students
print(result)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;{'지연'}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;차집합은 순서에 따라 결과가 달라진다는 점을 기억해야 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;딕셔너리와 집합의 차이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리와 집합은 모두 중괄호 &lt;code&gt;{}&lt;/code&gt;를 사용하기 때문에 처음에는 헷갈릴 수 있습니다. 하지만 구조가 다릅니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;딕셔너리&lt;/th&gt;
&lt;th&gt;집합&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;형태&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{key: value}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{value1, value2}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;목적&lt;/td&gt;
&lt;td&gt;key로 value 관리&lt;/td&gt;
&lt;td&gt;중복 없는 값 관리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;값 접근&lt;/td&gt;
&lt;td&gt;key 사용&lt;/td&gt;
&lt;td&gt;특정 위치 접근 불가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;중복&lt;/td&gt;
&lt;td&gt;key 중복 불가&lt;/td&gt;
&lt;td&gt;값 중복 불가&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;예시&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{&quot;name&quot;: &quot;철수&quot;}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{&quot;철수&quot;, &quot;영희&quot;}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리는 key와 value가 있습니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;user = {
    &quot;name&quot;: &quot;철수&quot;,
    &quot;age&quot;: 20
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;집합은 값만 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;names = {&quot;철수&quot;, &quot;영희&quot;, &quot;민수&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리는 특정 key의 값을 꺼낼 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;print(user[&quot;name&quot;])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;집합은 리스트처럼 &lt;code&gt;names[0]&lt;/code&gt;으로 값을 꺼낼 수 없습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;names = {&quot;철수&quot;, &quot;영희&quot;, &quot;민수&quot;}
print(names[0])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 오류가 발생합니다.&lt;/p&gt;
&lt;pre class=&quot;ceylon&quot;&gt;&lt;code&gt;TypeError: 'set' object is not subscriptable&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;집합은 순서를 기준으로 값을 다루는 자료형이 아니기 때문입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;딕셔너리와 리스트를 함께 사용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 코드에서는 딕셔너리와 리스트를 함께 사용하는 경우도 많습니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;students = [
    {&quot;name&quot;: &quot;철수&quot;, &quot;score&quot;: 90},
    {&quot;name&quot;: &quot;영희&quot;, &quot;score&quot;: 85},
    {&quot;name&quot;: &quot;민수&quot;, &quot;score&quot;: 92}
]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조는 리스트 안에 딕셔너리가 들어 있는 형태입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트는 여러 학생을 순서대로 담고 있습니다.&lt;br /&gt;각 학생의 정보는 딕셔너리로 표현됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 학생의 이름을 가져오려면 이렇게 쓸 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;print(students[0][&quot;name&quot;])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;철수&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;자주 발생하는 오류&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;없는 key를 사용할 때&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리에 없는 key를 대괄호로 가져오면 오류가 발생합니다.&lt;/p&gt;
&lt;pre class=&quot;nsis&quot;&gt;&lt;code&gt;user = {
    &quot;name&quot;: &quot;철수&quot;,
    &quot;age&quot;: 20
}

print(user[&quot;city&quot;])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;KeyError: 'city'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;없는 key일 수도 있다면 &lt;code&gt;get()&lt;/code&gt;을 사용하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;print(user.get(&quot;city&quot;, &quot;지역 정보 없음&quot;))&lt;/code&gt;&lt;/pre&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;pre class=&quot;ini&quot;&gt;&lt;code&gt;data = {&quot;name&quot;: &quot;철수&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 코드는 집합입니다.&lt;/p&gt;
&lt;pre class=&quot;haskell&quot;&gt;&lt;code&gt;data = {&quot;철수&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콜론 &lt;code&gt;:&lt;/code&gt;이 있으면 딕셔너리입니다.&lt;br /&gt;값만 있으면 집합입니다.&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;pre class=&quot;haskell&quot;&gt;&lt;code&gt;data = {}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 빈 딕셔너리입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빈 집합은 이렇게 만듭니다.&lt;/p&gt;
&lt;pre class=&quot;haskell&quot;&gt;&lt;code&gt;data = set()&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;딕셔너리와 집합 예제 정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리 예제를 다시 정리해보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;book = {
    &quot;title&quot;: &quot;파이썬 기초&quot;,
    &quot;price&quot;: 15000,
    &quot;author&quot;: &quot;홍길동&quot;
}

book[&quot;price&quot;] = 18000
book[&quot;publisher&quot;] = &quot;테크출판&quot;

print(book[&quot;title&quot;])
print(book.get(&quot;publisher&quot;))
print(book)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;파이썬 기초
테크출판
{'title': '파이썬 기초', 'price': 18000, 'author': '홍길동', 'publisher': '테크출판'}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 집합 예제입니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;tags = [&quot;python&quot;, &quot;basic&quot;, &quot;python&quot;, &quot;list&quot;, &quot;basic&quot;]

unique_tags = set(tags)

print(unique_tags)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 비슷합니다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;{'python', 'basic', 'list'}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;집합을 사용하면 중복된 값을 제거할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리와 집합은 모두 여러 값을 다룰 때 사용하는 자료형입니다.&lt;br /&gt;하지만 목적이 다릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리는 key와 value를 한 쌍으로 저장합니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;user = {
    &quot;name&quot;: &quot;철수&quot;,
    &quot;age&quot;: 20
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;key를 사용해서 값을 꺼낼 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;print(user[&quot;name&quot;])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;집합 set은 중복 없는 값을 저장할 때 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;numbers = {1, 2, 2, 3}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로 중복된 값은 하나만 남습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;{1, 2, 3}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보 단계에서는 이렇게 기억하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;딕셔너리: 이름표가 붙은 값 관리
집합 set: 중복 없는 값 관리&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트, 튜플, 딕셔너리, 집합까지 이해하면 파이썬의 기본 자료형 흐름을 어느 정도 잡은 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 조건에 따라 코드를 실행하는 &lt;b&gt;if 조건문&lt;/b&gt;을 알아보겠습니다.&lt;/p&gt;</description>
      <category>개발/python</category>
      <category>Python</category>
      <category>파이썬</category>
      <category>파이썬 set</category>
      <category>파이썬 기초</category>
      <category>파이썬 딕셔너리</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/25</guid>
      <comments>https://notebase.tistory.com/entry/python-dictionary-set-guide#entry25comment</comments>
      <pubDate>Thu, 21 May 2026 18:34:53 +0900</pubDate>
    </item>
    <item>
      <title>파이썬 리스트와 튜플 차이 쉽게 이해하기</title>
      <link>https://notebase.tistory.com/entry/python-list-tuple-guide</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 리스트와 튜플은 여러 값을 하나로 묶어서 다룰 때 사용하는 자료형입니다. 둘 다 순서가 있지만, 값을 바꿀 수 있는지에 따라 쓰임이 달라집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수는 값 하나를 저장할 때 많이 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;name = &quot;철수&quot;
age = 20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 이름 5개를 저장해야 한다면 어떻게 해야 할까요?&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;name1 = &quot;철수&quot;
name2 = &quot;영희&quot;
name3 = &quot;민수&quot;
name4 = &quot;지연&quot;
name5 = &quot;수진&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 변수 이름을 계속 늘리는 방식은 관리하기 어렵습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴 때 사용하는 것이 &lt;b&gt;리스트(list)&lt;/b&gt; 입니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;names = [&quot;철수&quot;, &quot;영희&quot;, &quot;민수&quot;, &quot;지연&quot;, &quot;수진&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트를 사용하면 여러 값을 하나의 변수에 담아 관리할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;리스트는 여러 값을 담는 자료형이다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트는 여러 값을 순서대로 저장하는 자료형입니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;fruits = [&quot;사과&quot;, &quot;바나나&quot;, &quot;딸기&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 &lt;code&gt;fruits&lt;/code&gt;라는 변수 안에는 세 개의 문자열이 들어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트 안에는 문자열뿐 아니라 숫자도 넣을 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;numbers = [10, 20, 30, 40]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로 다른 자료형을 함께 넣는 것도 가능합니다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;user = [&quot;철수&quot;, 20, True]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 초보 단계에서는 한 리스트 안에 같은 성격의 값을 넣는 방식이 이해하기 쉽습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;scores = [80, 90, 75]
names = [&quot;철수&quot;, &quot;영희&quot;, &quot;민수&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;파이썬 리스트 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트는 대괄호 &lt;code&gt;[]&lt;/code&gt;를 사용해서 만듭니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;리스트이름 = [값1, 값2, 값3]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;colors = [&quot;red&quot;, &quot;green&quot;, &quot;blue&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빈 리스트도 만들 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;items = []&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빈 리스트는 나중에 값을 추가할 때 자주 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;items = []

items.append(&quot;노트북&quot;)
items.append(&quot;마우스&quot;)

print(items)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[&quot;노트북&quot;, &quot;마우스&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;리스트에서 값 꺼내기: 인덱싱&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트 안의 값은 순서가 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;fruits = [&quot;사과&quot;, &quot;바나나&quot;, &quot;딸기&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 각각의 위치는 다음과 같습니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;값&lt;/th&gt;
&lt;th&gt;위치&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;사과&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;바나나&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;딸기&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬은 순서를 셀 때 &lt;b&gt;0부터 시작&lt;/b&gt;합니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;fruits = [&quot;사과&quot;, &quot;바나나&quot;, &quot;딸기&quot;]

print(fruits[0])
print(fruits[1])
print(fruits[2])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;사과
바나나
딸기&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 리스트에서 특정 위치의 값을 가져오는 것을 &lt;b&gt;인덱싱(indexing)&lt;/b&gt; 이라고 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;음수 인덱스 사용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 리스트는 음수 인덱스도 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;fruits = [&quot;사과&quot;, &quot;바나나&quot;, &quot;딸기&quot;]

print(fruits[-1])
print(fruits[-2])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;딸기
바나나&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;-1&lt;/code&gt;은 마지막 값을 의미합니다.&lt;br /&gt;&lt;code&gt;-2&lt;/code&gt;는 뒤에서 두 번째 값을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 값을 가져올 때는 &lt;code&gt;fruits[-1]&lt;/code&gt;처럼 쓰는 경우가 많습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;리스트 일부만 가져오기: 슬라이싱&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트에서 일부 값만 잘라서 가져올 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것을 &lt;b&gt;슬라이싱(slicing)&lt;/b&gt; 이라고 합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;numbers = [10, 20, 30, 40, 50]

print(numbers[1:4])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[20, 30, 40]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;numbers[1:4]&lt;/code&gt;는 인덱스 1부터 인덱스 4 전까지 가져오라는 뜻입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의할 점은 끝 위치의 값은 포함되지 않는다는 것입니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;numbers = [10, 20, 30, 40, 50]
# 인덱스:   0   1   2   3   4

print(numbers[1:4])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과는 인덱스 1, 2, 3에 해당하는 값입니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[20, 30, 40]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 가져오고 싶다면 시작 위치를 생략할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;numbers = [10, 20, 30, 40, 50]

print(numbers[:3])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[10, 20, 30]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 위치부터 끝까지 가져오고 싶다면 끝 위치를 생략할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;numbers = [10, 20, 30, 40, 50]

print(numbers[2:])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[30, 40, 50]&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;리스트에 값 추가하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트에 값을 추가할 때는 &lt;code&gt;append()&lt;/code&gt;를 많이 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;fruits = [&quot;사과&quot;, &quot;바나나&quot;]

fruits.append(&quot;딸기&quot;)

print(fruits)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[&quot;사과&quot;, &quot;바나나&quot;, &quot;딸기&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;append()&lt;/code&gt;는 리스트의 맨 뒤에 값을 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원하는 위치에 값을 넣고 싶다면 &lt;code&gt;insert()&lt;/code&gt;를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;fruits = [&quot;사과&quot;, &quot;딸기&quot;]

fruits.insert(1, &quot;바나나&quot;)

print(fruits)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[&quot;사과&quot;, &quot;바나나&quot;, &quot;딸기&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;insert(1, &quot;바나나&quot;)&lt;/code&gt;는 인덱스 1 위치에 &lt;code&gt;&quot;바나나&quot;&lt;/code&gt;를 넣으라는 뜻입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;리스트 값 수정하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트의 값은 나중에 바꿀 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;fruits = [&quot;사과&quot;, &quot;바나나&quot;, &quot;딸기&quot;]

fruits[1] = &quot;포도&quot;

print(fruits)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[&quot;사과&quot;, &quot;포도&quot;, &quot;딸기&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 인덱스 1 위치에 있던 &lt;code&gt;&quot;바나나&quot;&lt;/code&gt;가 &lt;code&gt;&quot;포도&quot;&lt;/code&gt;로 바뀌었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 리스트는 &lt;b&gt;변경 가능한 자료형&lt;/b&gt;입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;리스트 값 삭제하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트에서 값을 삭제하는 방법은 여러 가지가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 직관적인 방법은 &lt;code&gt;remove()&lt;/code&gt;입니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;fruits = [&quot;사과&quot;, &quot;바나나&quot;, &quot;딸기&quot;]

fruits.remove(&quot;바나나&quot;)

print(fruits)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[&quot;사과&quot;, &quot;딸기&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;remove()&lt;/code&gt;는 리스트에서 해당 값을 찾아 삭제합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스를 기준으로 삭제하고 싶다면 &lt;code&gt;pop()&lt;/code&gt;을 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;fruits = [&quot;사과&quot;, &quot;바나나&quot;, &quot;딸기&quot;]

fruits.pop(1)

print(fruits)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[&quot;사과&quot;, &quot;딸기&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;pop(1)&lt;/code&gt;은 인덱스 1에 있는 값을 삭제합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;pop()&lt;/code&gt;에 아무 값도 넣지 않으면 마지막 값을 삭제합니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;fruits = [&quot;사과&quot;, &quot;바나나&quot;, &quot;딸기&quot;]

fruits.pop()

print(fruits)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[&quot;사과&quot;, &quot;바나나&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;리스트 길이 확인하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트에 값이 몇 개 들어 있는지 확인할 때는 &lt;code&gt;len()&lt;/code&gt;을 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;fruits = [&quot;사과&quot;, &quot;바나나&quot;, &quot;딸기&quot;]

print(len(fruits))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;len()&lt;/code&gt;은 리스트뿐 아니라 문자열, 튜플 등 여러 자료형에서도 사용됩니다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;text = &quot;Python&quot;

print(len(text))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;6&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;리스트 안에 값이 있는지 확인하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트 안에 특정 값이 있는지 확인할 때는 &lt;code&gt;in&lt;/code&gt;을 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;fruits = [&quot;사과&quot;, &quot;바나나&quot;, &quot;딸기&quot;]

print(&quot;사과&quot; in fruits)
print(&quot;포도&quot; in fruits)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;True
False&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;in&lt;/code&gt;은 조건문과 함께 자주 사용됩니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;fruits = [&quot;사과&quot;, &quot;바나나&quot;, &quot;딸기&quot;]

if &quot;사과&quot; in fruits:
    print(&quot;사과가 있습니다.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건문은 이후 글에서 자세히 다루겠습니다.&lt;br /&gt;지금은 &lt;code&gt;in&lt;/code&gt;을 사용하면 리스트 안에 값이 있는지 확인할 수 있다는 정도만 이해하면 됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;튜플은 무엇인가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;튜플(tuple)은 리스트처럼 여러 값을 순서대로 담는 자료형입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 튜플은 소괄호 &lt;code&gt;()&lt;/code&gt;를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;point = (10, 20)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;튜플도 인덱싱이 가능합니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;point = (10, 20)

print(point[0])
print(point[1])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;10
20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;튜플도 슬라이싱할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;numbers = (10, 20, 30, 40, 50)

print(numbers[1:4])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;(20, 30, 40)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 보면 리스트와 거의 비슷해 보입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;차이는 값을 바꿀 수 있는지에서 나옵니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;튜플은 값을 바꿀 수 없다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트는 값을 수정할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;fruits = [&quot;사과&quot;, &quot;바나나&quot;, &quot;딸기&quot;]

fruits[0] = &quot;포도&quot;

print(fruits)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[&quot;포도&quot;, &quot;바나나&quot;, &quot;딸기&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 튜플은 한 번 만들면 내부 값을 바꿀 수 없습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;fruits = (&quot;사과&quot;, &quot;바나나&quot;, &quot;딸기&quot;)

fruits[0] = &quot;포도&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 오류가 발생합니다.&lt;/p&gt;
&lt;pre class=&quot;xquery&quot;&gt;&lt;code&gt;TypeError: 'tuple' object does not support item assignment&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;튜플은 &lt;b&gt;수정하기 어려운 리스트&lt;/b&gt;가 아니라, &lt;b&gt;변경할 수 없는 순서형 자료형&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 차이를 정확히 이해하는 것이 중요합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;리스트와 튜플의 차이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트와 튜플의 핵심 차이는 다음과 같습니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;리스트&lt;/th&gt;
&lt;th&gt;튜플&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;기호&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;값 변경&lt;/td&gt;
&lt;td&gt;가능&lt;/td&gt;
&lt;td&gt;불가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;값 추가&lt;/td&gt;
&lt;td&gt;가능&lt;/td&gt;
&lt;td&gt;불가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;값 삭제&lt;/td&gt;
&lt;td&gt;가능&lt;/td&gt;
&lt;td&gt;불가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;순서&lt;/td&gt;
&lt;td&gt;있음&lt;/td&gt;
&lt;td&gt;있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;인덱싱&lt;/td&gt;
&lt;td&gt;가능&lt;/td&gt;
&lt;td&gt;가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;슬라이싱&lt;/td&gt;
&lt;td&gt;가능&lt;/td&gt;
&lt;td&gt;가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트는 값을 추가, 수정, 삭제할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;numbers = [1, 2, 3]

numbers.append(4)
numbers[0] = 10

print(numbers)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[10, 2, 3, 4]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;튜플은 값을 바꿀 수 없습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;numbers = (1, 2, 3)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태에서 &lt;code&gt;numbers[0] = 10&lt;/code&gt;처럼 수정하려고 하면 오류가 발생합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;리스트를 써야 하는 경우&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트는 값이 나중에 바뀔 가능성이 있을 때 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 장바구니를 생각해볼 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;go&quot;&gt;&lt;code&gt;cart = []

cart.append(&quot;노트북&quot;)
cart.append(&quot;마우스&quot;)
cart.append(&quot;키보드&quot;)

print(cart)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[&quot;노트북&quot;, &quot;마우스&quot;, &quot;키보드&quot;]&lt;/code&gt;&lt;/pre&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;학생 점수 목록도 리스트로 다루기 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;scores = [80, 90, 75]

scores.append(100)

print(scores)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[80, 90, 75, 100]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값이 계속 변할 수 있다면 리스트를 사용하면 됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;튜플을 써야 하는 경우&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;튜플은 한 번 정한 값을 바꾸지 않을 때 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 좌표를 생각해볼 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;point = (10, 20)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요일처럼 고정된 값에도 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;weekdays = (&quot;월&quot;, &quot;화&quot;, &quot;수&quot;, &quot;목&quot;, &quot;금&quot;, &quot;토&quot;, &quot;일&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정값처럼 바뀌면 안 되는 데이터에도 튜플을 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;rgb = (255, 255, 255)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;튜플을 사용하면 이 데이터는 쉽게 수정되지 않는 값이라는 의도를 코드에서 드러낼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보 단계에서는 이렇게 판단하면 됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;나중에 값을 바꿀 수 있어야 한다면 리스트&lt;/li&gt;
&lt;li&gt;한 번 정한 값을 유지하고 싶다면 튜플&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;값 하나짜리 튜플을 만들 때 주의할 점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;튜플에서 헷갈리기 쉬운 부분이 하나 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값이 하나만 있는 튜플을 만들 때는 쉼표가 필요합니다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;a = (&quot;사과&quot;)
b = (&quot;사과&quot;,)

print(type(a))
print(type(b))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;&amp;lt;class 'str'&amp;gt;
&amp;lt;class 'tuple'&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;(&quot;사과&quot;)&lt;/code&gt;는 튜플이 아니라 문자열입니다.&lt;br /&gt;값 하나짜리 튜플은 &lt;code&gt;(&quot;사과&quot;,)&lt;/code&gt;처럼 쉼표를 붙여야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자도 마찬가지입니다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;a = (10)
b = (10,)

print(type(a))
print(type(b))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;&amp;lt;class 'int'&amp;gt;
&amp;lt;class 'tuple'&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보자에게는 자주 필요한 문법은 아니지만, 튜플을 공부할 때 한 번은 알아두는 것이 좋습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;리스트와 문자열은 닮은 점이 있다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 글에서 문자열을 다뤘다면 리스트가 조금 더 쉽게 느껴질 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열도 인덱싱할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;text = &quot;Python&quot;

print(text[0])
print(text[1])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;P
y&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열도 슬라이싱할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;text = &quot;Python&quot;

print(text[0:3])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;Pyt&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트도 마찬가지입니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;items = [&quot;P&quot;, &quot;y&quot;, &quot;t&quot;, &quot;h&quot;, &quot;o&quot;, &quot;n&quot;]

print(items[0])
print(items[0:3])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;P
['P', 'y', 't']&lt;/code&gt;&lt;/pre&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;다만 문자열은 문자들이 이어진 자료형이고, 리스트는 여러 값을 담을 수 있는 자료형입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;자주 발생하는 오류&lt;/h2&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;pre class=&quot;stylus&quot;&gt;&lt;code&gt;fruits = [&quot;사과&quot;, &quot;바나나&quot;, &quot;딸기&quot;]

print(fruits[3])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 오류가 발생합니다.&lt;/p&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;IndexError: list index out of range&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트의 마지막 인덱스는 값의 개수보다 1 작습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;fruits = [&quot;사과&quot;, &quot;바나나&quot;, &quot;딸기&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 리스트에는 값이 3개 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 인덱스는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;0, 1, 2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 &lt;code&gt;fruits[3]&lt;/code&gt;은 존재하지 않습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;remove()로 없는 값을 삭제하려는 경우&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;remove()&lt;/code&gt;는 리스트 안에 있는 값을 삭제합니다.&lt;br /&gt;그런데 없는 값을 삭제하려고 하면 오류가 발생합니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;fruits = [&quot;사과&quot;, &quot;바나나&quot;, &quot;딸기&quot;]

fruits.remove(&quot;포도&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;sas&quot;&gt;&lt;code&gt;ValueError: list.remove(x): x not in list&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삭제하기 전에 값이 있는지 확인하면 오류를 줄일 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;fruits = [&quot;사과&quot;, &quot;바나나&quot;, &quot;딸기&quot;]

if &quot;포도&quot; in fruits:
    fruits.remove(&quot;포도&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 조건문을 자세히 배우지 않았다면 이런 방식이 있다는 정도만 봐도 충분합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;리스트와 튜플 예제 정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 예제로 다시 정리해보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;fruits = [&quot;사과&quot;, &quot;바나나&quot;, &quot;딸기&quot;]

fruits.append(&quot;포도&quot;)
fruits[1] = &quot;수박&quot;
fruits.remove(&quot;딸기&quot;)

print(fruits)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[&quot;사과&quot;, &quot;수박&quot;, &quot;포도&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트는 값을 추가하고, 수정하고, 삭제할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 튜플입니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;colors = (&quot;red&quot;, &quot;green&quot;, &quot;blue&quot;)

print(colors[0])
print(colors[1:3])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;red
('green', 'blue')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;튜플은 인덱싱과 슬라이싱은 가능하지만, 값을 직접 수정할 수는 없습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트와 튜플은 여러 값을 하나로 묶어서 다룰 때 사용하는 자료형입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트는 대괄호 &lt;code&gt;[]&lt;/code&gt;를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;fruits = [&quot;사과&quot;, &quot;바나나&quot;, &quot;딸기&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;튜플은 소괄호 &lt;code&gt;()&lt;/code&gt;를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;fruits = (&quot;사과&quot;, &quot;바나나&quot;, &quot;딸기&quot;)&lt;/code&gt;&lt;/pre&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;차이는 값을 바꿀 수 있는지입니다.&lt;/p&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;초보 단계에서는 이렇게 기억하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;리스트: 나중에 바뀔 수 있는 값들의 묶음
튜플: 바뀌지 않아야 하는 값들의 묶음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 이름과 값이 짝을 이루는 자료형인 &lt;b&gt;딕셔너리(dictionary)&lt;/b&gt; 와, 중복을 제거할 때 자주 사용하는 &lt;b&gt;집합(set)&lt;/b&gt; 을 알아보겠습니다.&lt;/p&gt;</description>
      <category>개발/python</category>
      <category>Python</category>
      <category>파이썬</category>
      <category>파이썬 기초</category>
      <category>파이썬 리스트</category>
      <category>파이썬 튜플</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/20</guid>
      <comments>https://notebase.tistory.com/entry/python-list-tuple-guide#entry20comment</comments>
      <pubDate>Thu, 21 May 2026 15:17:59 +0900</pubDate>
    </item>
    <item>
      <title>파이썬 문자열 사용법: 초보자가 자주 헷갈리는 예제 중심 정리</title>
      <link>https://notebase.tistory.com/entry/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EB%AC%B8%EC%9E%90%EC%97%B4-%EC%82%AC%EC%9A%A9%EB%B2%95-%EC%B4%88%EB%B3%B4%EC%9E%90%EA%B0%80-%EC%9E%90%EC%A3%BC-%ED%97%B7%EA%B0%88%EB%A6%AC%EB%8A%94-%EC%98%88%EC%A0%9C-%EC%A4%91%EC%8B%AC-%EC%A0%95%EB%A6%AC</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 문자열은 글자, 단어, 문장을 다룰 때 사용하는 기본 자료형입니다. 이름, 이메일, 주소, 메시지, 파일명처럼 문자로 된 데이터는 대부분 문자열로 처리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 단순히 따옴표 안에 글자를 넣으면 된다고 생각하기 쉽습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제로 코드를 작성하다 보면 문자열을 합치거나, 일부만 자르거나, 변수와 함께 출력해야 하는 일이 많습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 파이썬 문자열을 처음 배우는 사람도 이해할 수 있도록 기본 사용법을 예제 중심으로 정리합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;파이썬 문자열이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열은 문자들이 순서대로 이어진 데이터입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서는 문자열을 &lt;code&gt;str&lt;/code&gt; 타입이라고 부릅니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;name = &quot;Python&quot;
message = &quot;Hello&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 &lt;code&gt;&quot;Python&quot;&lt;/code&gt;과 &lt;code&gt;&quot;Hello&quot;&lt;/code&gt;는 문자열입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열은 반드시 따옴표로 감싸야 합니다.&lt;br /&gt;따옴표 없이 쓰면 파이썬은 그것을 문자열이 아니라 변수 이름으로 해석합니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;text = Hello&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 오류가 납니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Hello&lt;/code&gt;라는 문자열을 저장하려면 아래처럼 작성해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;text = &quot;Hello&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문자열을 만드는 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서는 문자열을 만들 때 작은따옴표 또는 큰따옴표를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;text1 = 'Hello'
text2 = &quot;Hello&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘 다 문자열입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 둘 중 하나만 골라 일관되게 쓰면 됩니다.&lt;br /&gt;다만 문자열 안에 따옴표가 들어갈 때는 상황에 따라 선택하면 편합니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;sentence = &quot;I'm learning Python&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서는 문자열 안에 작은따옴표가 들어가므로 바깥쪽을 큰따옴표로 감쌌습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 문자열 안에 큰따옴표가 들어가면 바깥쪽을 작은따옴표로 감쌀 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;sentence = 'He said &quot;Hello&quot;'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 따옴표 때문에 생기는 오류를 줄일 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;여러 줄 문자열 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문장이 길거나 여러 줄로 된 문자열을 만들 때는 따옴표 3개를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;text = &quot;&quot;&quot;안녕하세요.
파이썬 문자열을 배우고 있습니다.
여러 줄 문장도 저장할 수 있습니다.&quot;&quot;&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은따옴표 3개도 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;text = '''첫 번째 줄
두 번째 줄
세 번째 줄'''&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 줄 안내문, 설명문, 긴 메시지를 변수에 저장할 때 사용할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문자열 출력하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열은 &lt;code&gt;print()&lt;/code&gt; 함수로 출력할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;print(&quot;Hello Python&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;Hello Python&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수에 저장한 문자열도 출력할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;name = &quot;Python&quot;
print(name)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;Python&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열과 변수를 함께 출력할 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;name = &quot;민수&quot;
print(&quot;안녕하세요&quot;, name)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;안녕하세요 민수&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;,&lt;/code&gt;를 사용하면 값 사이에 공백이 자동으로 들어갑니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문자열 합치기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열끼리는 &lt;code&gt;+&lt;/code&gt; 연산자로 합칠 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;first = &quot;Hello&quot;
second = &quot;Python&quot;

result = first + second
print(result)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;HelloPython&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 문자열이 그대로 붙어서 출력됩니다.&lt;br /&gt;중간에 공백을 넣고 싶다면 공백 문자열을 함께 더해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;result = first + &quot; &quot; + second
print(result)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;Hello Python&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열 합치기는 이름, 문장, 파일명 등을 만들 때 자주 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;first_name = &quot;Kim&quot;
last_name = &quot;Gwanghee&quot;

full_name = first_name + &quot; &quot; + last_name
print(full_name)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;Kim Gwanghee&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문자열과 숫자는 바로 더할 수 없다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보자가 자주 만나는 오류 중 하나입니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;age = 20
print(&quot;나이: &quot; + age)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 오류가 납니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열과 숫자는 자료형이 다르기 때문입니다.&lt;br /&gt;문자열과 숫자를 &lt;code&gt;+&lt;/code&gt;로 합치려면 숫자를 문자열로 바꿔야 합니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;age = 20
print(&quot;나이: &quot; + str(age))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;나이: 20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;str()&lt;/code&gt;은 값을 문자열로 바꿔주는 함수입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제로는 뒤에서 설명할 &lt;code&gt;f-string&lt;/code&gt;을 쓰는 편이 더 깔끔합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문자열 반복하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열은 &lt;code&gt;*&lt;/code&gt; 연산자로 반복할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;text = &quot;Hi&quot;
print(text * 3)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;HiHiHi&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구분선을 만들 때도 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;print(&quot;-&quot; * 20)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;--------------------&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 출력 형식을 만들 때 유용합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문자열 인덱싱&lt;/h2&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;이 위치 번호를 인덱스라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬의 인덱스는 &lt;code&gt;0&lt;/code&gt;부터 시작합니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;word = &quot;Python&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 글자의 위치는 다음과 같습니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;문자&lt;/th&gt;
&lt;th&gt;P&lt;/th&gt;
&lt;th&gt;y&lt;/th&gt;
&lt;th&gt;t&lt;/th&gt;
&lt;th&gt;h&lt;/th&gt;
&lt;th&gt;o&lt;/th&gt;
&lt;th&gt;n&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;인덱스&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 글자를 가져오려면 이렇게 씁니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;word = &quot;Python&quot;
print(word[0])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;P&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 번째 글자는 인덱스 &lt;code&gt;2&lt;/code&gt;입니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;print(word[2])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;t&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 배울 때 가장 헷갈리는 부분은 &amp;ldquo;첫 번째 글자 = 0번&amp;rdquo;이라는 점입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;음수 인덱스&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬은 뒤에서부터 글자를 가져올 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;word = &quot;Python&quot;
print(word[-1])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;n&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;-1&lt;/code&gt;은 마지막 글자를 의미합니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;print(word[-2])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;o&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뒤에서부터 접근할 때는 &lt;code&gt;-1&lt;/code&gt;, &lt;code&gt;-2&lt;/code&gt;, &lt;code&gt;-3&lt;/code&gt;처럼 사용합니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;문자&lt;/th&gt;
&lt;th&gt;P&lt;/th&gt;
&lt;th&gt;y&lt;/th&gt;
&lt;th&gt;t&lt;/th&gt;
&lt;th&gt;h&lt;/th&gt;
&lt;th&gt;o&lt;/th&gt;
&lt;th&gt;n&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;음수 인덱스&lt;/td&gt;
&lt;td&gt;-6&lt;/td&gt;
&lt;td&gt;-5&lt;/td&gt;
&lt;td&gt;-4&lt;/td&gt;
&lt;td&gt;-3&lt;/td&gt;
&lt;td&gt;-2&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 글자를 가져올 때는 음수 인덱스가 편합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문자열 슬라이싱&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;슬라이싱은 문자열의 일부를 잘라내는 방법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 형태는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;문자열[시작:끝]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의할 점은 &lt;code&gt;끝&lt;/code&gt; 위치의 글자는 포함되지 않는다는 것입니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;word = &quot;Python&quot;
print(word[0:2])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;Py&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;0&lt;/code&gt;번부터 시작해서 &lt;code&gt;2&lt;/code&gt;번 앞까지 가져옵니다.&lt;br /&gt;즉, 인덱스 &lt;code&gt;0&lt;/code&gt;과 &lt;code&gt;1&lt;/code&gt;에 해당하는 &lt;code&gt;&quot;Py&quot;&lt;/code&gt;만 가져옵니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;시작 위치 생략하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 자르고 싶다면 시작 위치를 생략할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;word = &quot;Python&quot;
print(word[:3])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;Pyt&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 인덱스 &lt;code&gt;3&lt;/code&gt; 앞까지 가져옵니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;끝 위치 생략하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 위치부터 끝까지 가져오고 싶다면 끝 위치를 생략합니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;word = &quot;Python&quot;
print(word[3:])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;hon&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스 &lt;code&gt;3&lt;/code&gt;부터 마지막까지 가져옵니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;전체 문자열 복사하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작과 끝을 모두 생략하면 전체 문자열을 가져옵니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;word = &quot;Python&quot;
print(word[:])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;Python&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실무에서 자주 쓰는 패턴은 아니지만, 슬라이싱 구조를 이해하는 데 도움이 됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문자열 안에 변수 넣기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열 안에 변수 값을 넣는 방법은 여러 가지가 있습니다.&lt;br /&gt;초보자에게는 &lt;code&gt;f-string&lt;/code&gt; 방식이 가장 이해하기 쉽습니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;name = &quot;민수&quot;
age = 20

print(f&quot;이름은 {name}이고, 나이는 {age}살입니다.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;이름은 민수이고, 나이는 20살입니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열 앞에 &lt;code&gt;f&lt;/code&gt;를 붙이고, 변수는 &lt;code&gt;{}&lt;/code&gt; 안에 넣습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식은 문자열과 숫자를 함께 출력할 때도 편합니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;price = 15000
count = 2

print(f&quot;가격은 {price}원이고, 수량은 {count}개입니다.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;가격은 15000원이고, 수량은 2개입니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;+&lt;/code&gt;로 문자열을 계속 합치는 것보다 읽기 쉽습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;f-string 안에서 계산하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;f-string&lt;/code&gt; 안에서는 간단한 계산도 할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;price = 15000
count = 2

print(f&quot;총 금액은 {price * count}원입니다.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;총 금액은 30000원입니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수 값을 문장에 자연스럽게 넣어야 할 때 많이 사용합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;자주 쓰는 문자열 메서드&lt;/h2&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;형태는 보통 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;gcode&quot;&gt;&lt;code&gt;문자열.메서드()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입문 단계에서 자주 쓰는 문자열 메서드를 몇 가지 살펴보겠습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;대문자와 소문자 바꾸기&lt;/h2&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;text = &quot;Python&quot;

print(text.upper())
print(text.lower())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;PYTHON
python&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;upper()&lt;/code&gt;는 대문자로 바꿉니다.&lt;br /&gt;&lt;code&gt;lower()&lt;/code&gt;는 소문자로 바꿉니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영문 검색, 비교, 정리 작업에서 자주 사용합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;공백 제거하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 입력한 값에는 앞뒤 공백이 들어갈 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;mel&quot;&gt;&lt;code&gt;text = &quot;  Python  &quot;
print(text.strip())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;Python&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;strip()&lt;/code&gt;은 문자열 양쪽의 불필요한 공백을 제거합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회원가입, 검색어 입력, 파일 처리처럼 사용자 입력을 다룰 때 자주 사용됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문자열 바꾸기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열 안의 특정 부분을 다른 문자열로 바꿀 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;text = &quot;I like Java&quot;
print(text.replace(&quot;Java&quot;, &quot;Python&quot;))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;axapta&quot;&gt;&lt;code&gt;I like Python&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;replace()&lt;/code&gt;는 첫 번째 값으로 찾을 문자열을 받고, 두 번째 값으로 바꿀 문자열을 받습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문자열 나누기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열을 특정 기준으로 나눌 때는 &lt;code&gt;split()&lt;/code&gt;을 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;text = &quot;apple,banana,orange&quot;
fruits = text.split(&quot;,&quot;)

print(fruits)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;scheme&quot;&gt;&lt;code&gt;['apple', 'banana', 'orange']&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;,&lt;/code&gt;를 기준으로 문자열이 나뉘고, 결과는 리스트로 만들어집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 리스트를 배우지 않았다면 &amp;ldquo;여러 값을 담는 자료형으로 바뀐다&amp;rdquo; 정도로 이해하면 됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문자열 길이 구하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열의 길이는 &lt;code&gt;len()&lt;/code&gt; 함수로 구합니다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;text = &quot;Python&quot;
print(len(text))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;6&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열에 글자가 몇 개 들어 있는지 확인할 때 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한글도 글자 수 기준으로 계산할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;text = &quot;파이썬&quot;
print(len(text))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;3&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;특정 문자열이 포함되어 있는지 확인하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열 안에 특정 글자나 단어가 들어 있는지 확인할 때는 &lt;code&gt;in&lt;/code&gt;을 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;text = &quot;I am learning Python&quot;

print(&quot;Python&quot; in text)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;True&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포함되어 있지 않으면 &lt;code&gt;False&lt;/code&gt;가 나옵니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;print(&quot;Java&quot; in text)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;False&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색 기능, 필터링, 조건문에서 자주 사용됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;초보자가 자주 하는 실수&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 따옴표를 빼먹는 경우&lt;/h2&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;text = Hello&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 오류가 납니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열은 반드시 따옴표로 감싸야 합니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;text = &quot;Hello&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 문자열과 숫자를 바로 더하는 경우&lt;/h2&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;age = 20
print(&quot;나이: &quot; + age)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열과 숫자는 바로 더할 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래처럼 바꿔야 합니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;age = 20
print(&quot;나이: &quot; + str(age))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 &lt;code&gt;f-string&lt;/code&gt;을 사용하는 것이 더 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;age = 20
print(f&quot;나이: {age}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 인덱스가 1부터 시작한다고 생각하는 경우&lt;/h2&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;word = &quot;Python&quot;
print(word[1])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 첫 번째 글자인 &lt;code&gt;P&lt;/code&gt;가 아니라 두 번째 글자인 &lt;code&gt;y&lt;/code&gt;를 출력합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 인덱스는 &lt;code&gt;0&lt;/code&gt;부터 시작합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 글자는 아래처럼 가져옵니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;print(word[0])&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 슬라이싱의 끝 위치가 포함된다고 생각하는 경우&lt;/h2&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;word = &quot;Python&quot;
print(word[0:3])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;Pyt&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스 &lt;code&gt;0&lt;/code&gt;, &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;2&lt;/code&gt;까지만 포함됩니다.&lt;br /&gt;끝 위치인 &lt;code&gt;3&lt;/code&gt;은 포함되지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;슬라이싱은 처음에는 헷갈리지만, 아래처럼 기억하면 편합니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[시작 위치 : 끝나기 직전 위치]&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문자열은 직접 수정할 수 없다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 문자열은 한 번 만들어지면 내부 글자를 직접 바꿀 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래 코드는 오류가 납니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;word = &quot;Python&quot;
word[0] = &quot;J&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열의 일부를 바꾸고 싶다면 새 문자열을 만들어야 합니다.&lt;/p&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;word = &quot;Python&quot;
new_word = &quot;J&quot; + word[1:]

print(new_word)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;Jython&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 &lt;code&gt;replace()&lt;/code&gt;를 사용할 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;haxe&quot;&gt;&lt;code&gt;word = &quot;Python&quot;
new_word = word.replace(&quot;P&quot;, &quot;J&quot;)

print(new_word)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;Jython&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열은 수정하는 것이 아니라, 바뀐 결과를 새로 만든다고 이해하면 됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;간단한 예제로 정리하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드는 문자열에서 자주 쓰는 기능을 한 번에 보여줍니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;name = &quot;kim&quot;
language = &quot;Python&quot;

print(f&quot;{name.upper()}님은 {language}을 배우고 있습니다.&quot;)
print(language[0])
print(language[:3])
print(len(language))
print(&quot;Py&quot; in language)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;KIM님은 Python을 배우고 있습니다.
P
Pyt
6
True&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예제 안에는 다음 개념이 들어 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;upper()&lt;/code&gt;로 대문자 변환&lt;/li&gt;
&lt;li&gt;&lt;code&gt;f-string&lt;/code&gt;으로 변수 넣기&lt;/li&gt;
&lt;li&gt;인덱싱으로 첫 글자 가져오기&lt;/li&gt;
&lt;li&gt;슬라이싱으로 일부 문자열 가져오기&lt;/li&gt;
&lt;li&gt;&lt;code&gt;len()&lt;/code&gt;으로 길이 구하기&lt;/li&gt;
&lt;li&gt;&lt;code&gt;in&lt;/code&gt;으로 포함 여부 확인하기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열 기초를 익힐 때는 이런 짧은 예제를 직접 실행해보는 것이 좋습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 문자열은 단순히 글자를 저장하는 기능만 있는 것이 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열을 합치고, 자르고, 바꾸고, 검색하고, 변수와 함께 출력하는 작업은 거의 모든 파이썬 코드에서 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 특히 아래 개념을 확실히 익히는 것이 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;문자열 만들기
문자열 합치기
인덱싱
슬라이싱
f-string
자주 쓰는 문자열 메서드&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열이 익숙해지면 리스트, 조건문, 반복문을 배울 때도 훨씬 수월해집니다.&lt;/p&gt;</description>
      <category>개발/python</category>
      <category>Python</category>
      <category>문자열</category>
      <category>코딩입문</category>
      <category>파이썬</category>
      <category>파이썬기초</category>
      <category>파이썬문자열</category>
      <category>프로그래밍기초</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/19</guid>
      <comments>https://notebase.tistory.com/entry/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EB%AC%B8%EC%9E%90%EC%97%B4-%EC%82%AC%EC%9A%A9%EB%B2%95-%EC%B4%88%EB%B3%B4%EC%9E%90%EA%B0%80-%EC%9E%90%EC%A3%BC-%ED%97%B7%EA%B0%88%EB%A6%AC%EB%8A%94-%EC%98%88%EC%A0%9C-%EC%A4%91%EC%8B%AC-%EC%A0%95%EB%A6%AC#entry19comment</comments>
      <pubDate>Thu, 21 May 2026 10:13:56 +0900</pubDate>
    </item>
    <item>
      <title>파이썬 변수와 자료형 쉽게 이해하기: 초보자를 위한 Python 기초</title>
      <link>https://notebase.tistory.com/entry/python-variables-data-types</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 변수와 자료형이 헷갈리나요? Python 3.14.5 공식 문서 흐름에 맞춰 변수, int, float, str, bool, None, list, dict, type() 사용법까지 초보자 기준으로 정리했습니다.&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;1200&quot; data-origin-height=&quot;630&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/y3ibQ/dJMcabK5v5p/tkhp0jKe0Gk78FATfdKEjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y3ibQ/dJMcabK5v5p/tkhp0jKe0Gk78FATfdKEjK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y3ibQ/dJMcabK5v5p/tkhp0jKe0Gk78FATfdKEjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy3ibQ%2FdJMcabK5v5p%2Ftkhp0jKe0Gk78FATfdKEjK%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;1200&quot; height=&quot;630&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;630&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 변수와 자료형은 Python을 처음 배울 때 가장 먼저 만나는 개념입니다.&lt;br /&gt;그런데 생각보다 많은 초보자가 여기서 헷갈립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;name = &quot;Python&quot;&lt;/code&gt; 같은 코드는 짧아 보입니다.&lt;br /&gt;하지만 이 한 줄 안에는 &lt;b&gt;변수, 값, 자료형, 대입&lt;/b&gt;이라는 중요한 개념이 모두 들어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 2026년 5월 기준 Python 3.14.5 공식 문서 흐름에 맞춰, 파이썬 변수와 자료형을 초보자 기준으로 정리해보겠습니다.&lt;br /&gt;전공자식 설명보다 &amp;ldquo;실제로 코드를 읽을 때 어떻게 이해하면 되는지&amp;rdquo;에 초점을 맞췄습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;이 글에서 다루는 내용&lt;/b&gt;&lt;/h2&gt;
&lt;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;ldquo;상자&amp;rdquo;보다 &amp;ldquo;이름표&amp;rdquo;에 가까운지&lt;/li&gt;
&lt;li&gt;자료형이 필요한 이유&lt;/li&gt;
&lt;li&gt;&lt;code&gt;int&lt;/code&gt;, &lt;code&gt;float&lt;/code&gt;, &lt;code&gt;str&lt;/code&gt;, &lt;code&gt;bool&lt;/code&gt;, &lt;code&gt;None&lt;/code&gt; 이해하기&lt;/li&gt;
&lt;li&gt;&lt;code&gt;list&lt;/code&gt;, &lt;code&gt;tuple&lt;/code&gt;, &lt;code&gt;dict&lt;/code&gt;, &lt;code&gt;set&lt;/code&gt; 차이&lt;/li&gt;
&lt;li&gt;&lt;code&gt;type()&lt;/code&gt; 함수로 자료형 확인하는 방법&lt;/li&gt;
&lt;li&gt;초보자가 자주 하는 실수&lt;/li&gt;
&lt;li&gt;직접 풀어볼 수 있는 연습문제&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;변수는 값을 담는 상자일까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 코딩을 배울 때 변수는 보통 &amp;ldquo;값을 담는 상자&amp;rdquo;라고 설명합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래 코드를 보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;name = &quot;Python&quot;
age = 35&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드를 처음 보면 이렇게 이해하기 쉽습니다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;name이라는 상자에 &quot;Python&quot;을 넣었다.
age라는 상자에 35를 넣었다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입문 단계에서는 이 설명도 도움이 됩니다.&lt;br /&gt;하지만 파이썬을 조금 더 정확하게 이해하려면 이렇게 보는 편이 낫습니다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;name이라는 이름이 &quot;Python&quot;이라는 값을 가리킨다.
age라는 이름이 35라는 값을 가리킨다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서 변수는 값에 붙이는 &lt;b&gt;이름표&lt;/b&gt;에 가깝습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCY5fA/dJMcafmobtE/kbyI3iHIgJe9xxpsXzLaZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCY5fA/dJMcafmobtE/kbyI3iHIgJe9xxpsXzLaZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCY5fA/dJMcafmobtE/kbyI3iHIgJe9xxpsXzLaZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCY5fA%2FdJMcafmobtE%2FkbyI3iHIgJe9xxpsXzLaZ0%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;1200&quot; height=&quot;800&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 문서에서도 파이썬의 모든 데이터는 객체로 표현된다고 설명합니다. 객체는 각각 정체성(identity), 자료형(type), 값(value)을 가집니다. 초보자 입장에서는 이 말을 전부 외울 필요는 없습니다. 우선은 &amp;ldquo;변수 이름이 값 객체를 가리킨다&amp;rdquo; 정도로 이해하면 충분합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;변수 만들기: 대입 연산자 &lt;code&gt;=&lt;/code&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서 변수는 보통 &lt;code&gt;=&lt;/code&gt; 기호로 만듭니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;score = 90&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;=&lt;/code&gt;는 수학의 &amp;ldquo;같다&amp;rdquo;와 완전히 같은 의미가 아닙니다.&lt;br /&gt;코딩에서는 보통 &lt;b&gt;오른쪽 값을 왼쪽 이름에 연결한다&lt;/b&gt;는 뜻입니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;score = 90&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 이렇게 읽으면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;90이라는 값을 score라는 이름으로 부르겠다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 아래 코드도 가능합니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;score = 90
score = 95

print(score)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;95&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 &lt;code&gt;score&lt;/code&gt;가 90을 가리켰지만, 다음 줄에서 95를 가리키도록 다시 연결했기 때문입니다.&lt;br /&gt;이것을 &lt;b&gt;재대입&lt;/b&gt;이라고 볼 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;변수 이름을 지을 때 기본 규칙&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수 이름은 아무렇게나 지으면 안 됩니다.&lt;br /&gt;초보자는 아래 정도만 기억해도 충분합니다.&lt;/p&gt;
&lt;table style=&quot;height: 115px;&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;thead&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;th style=&quot;height: 20px;&quot;&gt;규칙&lt;/th&gt;
&lt;th style=&quot;height: 20px;&quot;&gt;예시&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;숫자로 시작할 수 없음&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;code&gt;1name&lt;/code&gt;은 안 됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;공백을 넣을 수 없음&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;code&gt;user name&lt;/code&gt;은 안 됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;대소문자를 구분함&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;code&gt;age&lt;/code&gt;와 &lt;code&gt;Age&lt;/code&gt;는 다름&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;예약어는 피해야 함&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;code&gt;if&lt;/code&gt;, &lt;code&gt;for&lt;/code&gt;, &lt;code&gt;class&lt;/code&gt; 같은 단어는 안 됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;의미 있는 이름이 좋음&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;code&gt;a&lt;/code&gt;보다 &lt;code&gt;user_age&lt;/code&gt;가 읽기 쉬움&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 이런 변수 이름은 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;user_name = &quot;민수&quot;
user_age = 25
is_student = True&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 이런 이름은 피하는 것이 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;a = &quot;민수&quot;
b = 25
c = True&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드가 짧을 때는 괜찮아 보이지만, 줄이 길어지면 &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;b&lt;/code&gt;, &lt;code&gt;c&lt;/code&gt;가 무엇을 뜻하는지 금방 헷갈립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서는 한글 변수명도 동작할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;이름 = &quot;민수&quot;
나이 = 25&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 처음 공부할 때는 영어 변수명에 익숙해지는 편이 좋습니다.&lt;br /&gt;예제, 문서, 오류 메시지, 협업 코드가 대부분 영어 기준으로 작성되기 때문입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;자료형이란 무엇일까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자료형은 쉽게 말해 &lt;b&gt;값의 종류&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 값을 비교해보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;age = 25
name = &quot;민수&quot;
is_student = True&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;겉보기에는 모두 변수입니다.&lt;br /&gt;하지만 값 객체의 종류는 다릅니다.&lt;/p&gt;
&lt;table style=&quot;height: 77px;&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;thead&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;th style=&quot;height: 20px;&quot;&gt;이름&lt;/th&gt;
&lt;th style=&quot;height: 20px;&quot; align=&quot;right&quot;&gt;값&lt;/th&gt;
&lt;th style=&quot;height: 20px;&quot;&gt;값의 자료형&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;code&gt;age&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot; align=&quot;right&quot;&gt;&lt;code&gt;25&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;정수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot; align=&quot;right&quot;&gt;&lt;code&gt;&quot;민수&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;문자열&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;code&gt;is_student&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot; align=&quot;right&quot;&gt;&lt;code&gt;True&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;불리언&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;b&gt;값의 자료형에 따라 할 수 있는 일이 달라지기 때문입니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 숫자는 계산할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;price = 10000
discount = 2000

print(price - discount)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;8000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열은 글자를 이어붙일 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;first_name = &quot;김&quot;
last_name = &quot;민수&quot;

print(first_name + last_name)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;김민수&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 숫자와 문자열을 그대로 더하려고 하면 오류가 납니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;age = 25

print(&quot;나이: &quot; + age)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 오류가 납니다.&lt;br /&gt;&lt;code&gt;&quot;나이: &quot;&lt;/code&gt;는 문자열이고, &lt;code&gt;age&lt;/code&gt;가 가리키는 &lt;code&gt;25&lt;/code&gt;는 정수 객체이기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결하려면 숫자를 문자열로 바꿔야 합니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;age = 25

print(&quot;나이: &quot; + str(age))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;나이: 25&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;code&gt;type()&lt;/code&gt; 함수로 자료형 확인하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자료형이 헷갈릴 때는 &lt;code&gt;type()&lt;/code&gt; 함수를 쓰면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;name = &quot;Python&quot;
age = 35
height = 175.5
is_easy = True

print(type(name))
print(type(age))
print(type(height))
print(type(is_easy))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 비슷합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;&amp;lt;class 'str'&amp;gt;
&amp;lt;class 'int'&amp;gt;
&amp;lt;class 'float'&amp;gt;
&amp;lt;class 'bool'&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 &lt;code&gt;&amp;lt;class 'str'&amp;gt;&lt;/code&gt; 같은 표현이 낯설 수 있습니다.&lt;br /&gt;간단히 이렇게 이해하면 됩니다.&lt;/p&gt;
&lt;table style=&quot;height: 96px;&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;thead&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;th style=&quot;height: 20px;&quot;&gt;출력&lt;/th&gt;
&lt;th style=&quot;height: 20px;&quot;&gt;뜻&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;code&gt;&amp;lt;class 'str'&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;문자열&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;code&gt;&amp;lt;class 'int'&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;정수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;code&gt;&amp;lt;class 'float'&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;실수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;code&gt;&amp;lt;class 'bool'&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;참/거짓&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;type()&lt;/code&gt;은 초보자가 오류를 찾을 때도 매우 유용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래 코드를 보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;price = &quot;10000&quot;
delivery_fee = 3000

print(price + delivery_fee)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 오류가 납니다.&lt;br /&gt;겉으로 보기에는 &lt;code&gt;10000&lt;/code&gt;이 숫자처럼 보이지만, 따옴표 안에 있기 때문에 문자열입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인해보면 바로 알 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;price = &quot;10000&quot;

print(type(price))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;&amp;lt;class 'str'&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;파이썬 기본 값&amp;middot;자료형 한눈에 보기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 모든 자료형을 외울 필요는 없습니다.&lt;br /&gt;입문 단계에서는 아래 자료형과 값을 먼저 이해하면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;900&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Tfm6k/dJMcaaejdsb/QFZkuVaKToZ4Lih7wL8IhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Tfm6k/dJMcaaejdsb/QFZkuVaKToZ4Lih7wL8IhK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Tfm6k/dJMcaaejdsb/QFZkuVaKToZ4Lih7wL8IhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTfm6k%2FdJMcaaejdsb%2FQFZkuVaKToZ4Lih7wL8IhK%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;1200&quot; height=&quot;900&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;900&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;값/자료형&lt;/th&gt;
&lt;th&gt;뜻&lt;/th&gt;
&lt;th&gt;예시&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;int&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;정수&lt;/td&gt;
&lt;td&gt;&lt;code&gt;10&lt;/code&gt;, &lt;code&gt;-3&lt;/code&gt;, &lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;float&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;실수&lt;/td&gt;
&lt;td&gt;&lt;code&gt;3.14&lt;/code&gt;, &lt;code&gt;0.5&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;문자열&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&quot;Python&quot;&lt;/code&gt;, &lt;code&gt;&quot;안녕&quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bool&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;참/거짓&lt;/td&gt;
&lt;td&gt;&lt;code&gt;True&lt;/code&gt;, &lt;code&gt;False&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;None&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;값의 부재를 나타내는 특별한 값&lt;/td&gt;
&lt;td&gt;&lt;code&gt;None&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;수정 가능한 순서형 자료&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[1, 2, 3]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tuple&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;수정 불가능한 순서형 자료&lt;/td&gt;
&lt;td&gt;&lt;code&gt;(1, 2, 3)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dict&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;키와 값의 매핑&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{&quot;name&quot;: &quot;민수&quot;}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;set&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;중복 없는 집합&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{1, 2, 3}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 문서에서는 숫자형, 시퀀스형, 매핑형 같은 큰 분류로 자료형을 설명합니다. 초보자라면 처음부터 분류명을 외우기보다, 위 표의 예시를 직접 실행해보는 것이 더 좋습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;숫자 자료형: &lt;code&gt;int&lt;/code&gt;와 &lt;code&gt;float&lt;/code&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서 숫자는 크게 정수와 실수로 나눠서 이해하면 됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;code&gt;int&lt;/code&gt;: 정수&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;int&lt;/code&gt;는 소수점이 없는 숫자입니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;age = 25
count = 3
temperature = -5

print(type(age))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;&amp;lt;class 'int'&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;int&lt;/code&gt;는 더하기, 빼기, 곱하기, 나누기 같은 계산에 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;a = 10
b = 3

print(a + b)
print(a - b)
print(a * b)
print(a / b)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;13
7
30
3.3333333333333335&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나누기 결과는 소수점이 포함될 수 있기 때문에 &lt;code&gt;float&lt;/code&gt;로 나옵니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;code&gt;float&lt;/code&gt;: 실수&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;float&lt;/code&gt;는 소수점이 있는 숫자입니다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;height = 175.5
weight = 68.2

print(type(height))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;&amp;lt;class 'float'&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;float&lt;/code&gt;를 쓸 때 초보자가 자주 놀라는 예시가 있습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;print(0.1 + 0.2)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 보통 다음처럼 나옵니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;0.30000000000000004&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 0.3이 아니냐고 느낄 수 있습니다.&lt;br /&gt;이건 파이썬만의 문제가 아니라 컴퓨터가 소수를 이진수로 표현하는 방식 때문에 생기는 현상입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입문 단계에서는 이렇게 기억하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;돈 계산처럼 정확한 소수 계산이 필요한 경우에는 float만 믿으면 안 된다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 이 정도만 알아도 충분합니다.&lt;br /&gt;나중에 돈 계산이나 정밀 계산을 다룰 때 &lt;code&gt;Decimal&lt;/code&gt; 같은 도구를 따로 배우면 됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;문자열 자료형: &lt;code&gt;str&lt;/code&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;str&lt;/code&gt;은 문자열입니다.&lt;br /&gt;문자열은 글자, 단어, 문장을 다룰 때 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;name = &quot;민수&quot;
language = &quot;Python&quot;
message = &quot;안녕하세요&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열은 작은따옴표와 큰따옴표를 모두 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;text1 = 'Python'
text2 = &quot;Python&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘 다 문자열입니다.&lt;br /&gt;중요한 것은 따옴표 안에 들어가면 숫자처럼 보여도 문자열이 된다는 점입니다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;number1 = 100
number2 = &quot;100&quot;

print(type(number1))
print(type(number2))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;&amp;lt;class 'int'&amp;gt;
&amp;lt;class 'str'&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;number1&lt;/code&gt;은 숫자입니다.&lt;br /&gt;&lt;code&gt;number2&lt;/code&gt;는 문자열입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문자열은 더하기로 이어붙일 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;greeting = &quot;안녕하세요&quot;
name = &quot;민수&quot;

print(greeting + &quot;, &quot; + name + &quot;님&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;안녕하세요, 민수님&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 실무에서는 문자열을 이어붙일 때 &lt;code&gt;f-string&lt;/code&gt;을 자주 씁니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;name = &quot;민수&quot;
age = 25

print(f&quot;{name}님의 나이는 {age}살입니다.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;민수님의 나이는 25살입니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보자라면 문자열 더하기보다 &lt;code&gt;f-string&lt;/code&gt; 방식에 빨리 익숙해지는 것이 좋습니다.&lt;br /&gt;숫자와 문자열을 섞어 출력할 때 훨씬 편합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;참과 거짓: &lt;code&gt;bool&lt;/code&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;bool&lt;/code&gt;은 참과 거짓을 나타내는 자료형입니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;is_student = True
is_adult = False

print(type(is_student))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;&amp;lt;class 'bool'&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의할 점은 &lt;code&gt;True&lt;/code&gt;와 &lt;code&gt;False&lt;/code&gt;의 첫 글자가 대문자라는 것입니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;is_done = True&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래처럼 쓰면 안 됩니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;is_done = true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;true&lt;/code&gt;는 파이썬에서 참값으로 인식되지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;bool&lt;/code&gt;은 조건문에서 자주 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;is_logged_in = True

if is_logged_in:
    print(&quot;환영합니다.&quot;)
else:
    print(&quot;로그인이 필요합니다.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;환영합니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서는 &lt;code&gt;0&lt;/code&gt;, 빈 문자열, 빈 리스트 같은 값이 조건문에서 거짓처럼 취급될 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;name = &quot;&quot;

if name:
    print(&quot;이름이 있습니다.&quot;)
else:
    print(&quot;이름이 비어 있습니다.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;이름이 비어 있습니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 개념은 나중에 조건문을 배울 때 자주 나옵니다.&lt;br /&gt;지금은 &amp;ldquo;비어 있으면 거짓처럼 볼 수 있다&amp;rdquo; 정도만 기억하면 됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;값이 없음을 나타내는 &lt;code&gt;None&lt;/code&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;None&lt;/code&gt;은 &amp;ldquo;값이 없다&amp;rdquo; 또는 &amp;ldquo;아직 값이 정해지지 않았다&amp;rdquo;는 뜻으로 자주 쓰이는 특별한 값입니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;result = None

print(type(result))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;&amp;lt;class 'NoneType'&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점이 있습니다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;None은 값입니다.
NoneType은 None의 타입입니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &lt;code&gt;None&lt;/code&gt;은 값의 부재를 나타내는 특별한 객체이고, &lt;code&gt;type(None)&lt;/code&gt;을 실행하면 &lt;code&gt;&amp;lt;class 'NoneType'&amp;gt;&lt;/code&gt;이 출력됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;None&lt;/code&gt;은 0이나 빈 문자열과 다릅니다.&lt;/p&gt;
&lt;table style=&quot;height: 77px;&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;thead&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;th style=&quot;height: 20px;&quot;&gt;값&lt;/th&gt;
&lt;th style=&quot;height: 20px;&quot;&gt;의미&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;숫자 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;code&gt;&quot;&quot;&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;비어 있는 문자열&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;&lt;code&gt;None&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;height: 19px;&quot;&gt;값이 아직 없음을 나타내는 특별한 값&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아직 사용자의 이메일을 입력받지 않았다면 이렇게 둘 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;email = None&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나중에 이메일이 생기면 다시 값을 넣을 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;email = &quot;user@example.com&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보자는 &lt;code&gt;None&lt;/code&gt;을 &amp;ldquo;아직 정해지지 않은 값&amp;rdquo; 정도로 이해하면 됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;여러 값을 담는 &lt;code&gt;list&lt;/code&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;list&lt;/code&gt;는 여러 값을 순서대로 담을 때 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;fruits = [&quot;apple&quot;, &quot;banana&quot;, &quot;orange&quot;]

print(fruits)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;scheme&quot;&gt;&lt;code&gt;['apple', 'banana', 'orange']&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트는 순서가 있기 때문에 위치 번호로 값을 꺼낼 수 있습니다.&lt;br /&gt;파이썬의 위치 번호는 0부터 시작합니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;fruits = [&quot;apple&quot;, &quot;banana&quot;, &quot;orange&quot;]

print(fruits[0])
print(fruits[1])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;apple
banana&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트는 값을 추가하거나 바꿀 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;fruits = [&quot;apple&quot;, &quot;banana&quot;]

fruits.append(&quot;orange&quot;)
fruits[0] = &quot;grape&quot;

print(fruits)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;scheme&quot;&gt;&lt;code&gt;['grape', 'banana', 'orange']&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 값을 바꿀 수 있는 성질을 &lt;b&gt;mutable&lt;/b&gt;, 즉 수정 가능하다고 표현합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;수정 불가능한 순서형 자료: &lt;code&gt;tuple&lt;/code&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;tuple&lt;/code&gt;은 리스트처럼 여러 값을 순서대로 담을 수 있지만, 한 번 만든 뒤에는 안의 항목을 바꿀 수 없는 자료형입니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;point = (10, 20)

print(point[0])
print(point[1])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;10
20&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;튜플은 보통 바뀌면 안 되는 묶음을 표현할 때 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 좌표처럼 두 값이 함께 움직이는 경우입니다.&lt;/p&gt;
&lt;pre class=&quot;crmsh&quot;&gt;&lt;code&gt;location = (37.5665, 126.9780)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트와 튜플 차이는 이렇게 정리할 수 있습니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;자료형&lt;/th&gt;
&lt;th&gt;특징&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;순서가 있고, 값을 추가하거나 바꿀 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tuple&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;순서가 있지만, 한 번 만든 뒤 항목을 바꿀 수 없음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보 단계에서는 &lt;code&gt;list&lt;/code&gt;를 더 자주 쓰게 됩니다.&lt;br /&gt;튜플은 &amp;ldquo;수정 불가능한 순서형 자료&amp;rdquo;라고 기억하면 됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;키와 값으로 저장하는 &lt;code&gt;dict&lt;/code&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;dict&lt;/code&gt;는 키와 값을 묶어서 저장하는 자료형입니다.&lt;br /&gt;딕셔너리라고 읽습니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;user = {
    &quot;name&quot;: &quot;민수&quot;,
    &quot;age&quot;: 25,
    &quot;is_student&quot;: True
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조는 실제 데이터와 비슷합니다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;이름: 민수
나이: 25
학생 여부: True&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리에서 값을 꺼낼 때는 키를 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;user = {
    &quot;name&quot;: &quot;민수&quot;,
    &quot;age&quot;: 25
}

print(user[&quot;name&quot;])
print(user[&quot;age&quot;])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;민수
25&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리는 API 응답, 설정값, 사용자 정보, 상품 정보 같은 데이터를 다룰 때 자주 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 블로그 글 정보를 표현하면 이렇게 쓸 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;post = {
    &quot;title&quot;: &quot;파이썬 변수와 자료형&quot;,
    &quot;category&quot;: &quot;Python&quot;,
    &quot;views&quot;: 120
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보자가 &lt;code&gt;dict&lt;/code&gt;를 어렵게 느끼는 이유는 대괄호와 중괄호가 섞여 나오기 때문입니다.&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;list: []
dict: {}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 차이만 먼저 기억해도 충분합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;중복을 제거하는 &lt;code&gt;set&lt;/code&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;set&lt;/code&gt;은 중복 없는 값을 모을 때 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;numbers = {1, 2, 2, 3, 3, 3}

print(numbers)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 비슷합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;{1, 2, 3}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중복된 값이 하나로 정리됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 방문자가 검색한 키워드 목록에서 중복을 제거하고 싶다면 이런 식으로 생각할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;keywords = [&quot;Python&quot;, &quot;ChatGPT&quot;, &quot;Python&quot;, &quot;Docker&quot;]

unique_keywords = set(keywords)

print(unique_keywords)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과는 순서가 다르게 보일 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;{'Docker', 'Python', 'ChatGPT'}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;set&lt;/code&gt;은 중복 제거에는 편하지만, 순서를 중요하게 다루는 자료형은 아닙니다.&lt;br /&gt;처음에는 &amp;ldquo;중복 없는 집합&amp;rdquo; 정도로 이해하면 됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;list와 dict는 특히 중요하다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입문 단계에서 가장 중요한 자료형을 고르라면 &lt;code&gt;list&lt;/code&gt;와 &lt;code&gt;dict&lt;/code&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python으로 자동화, 데이터 정리, API 사용을 하다 보면 이 둘을 자주 만나게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 여러 명의 사용자 정보를 저장한다고 해보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;users = [
    {&quot;name&quot;: &quot;민수&quot;, &quot;age&quot;: 25},
    {&quot;name&quot;: &quot;지은&quot;, &quot;age&quot;: 30},
    {&quot;name&quot;: &quot;현우&quot;, &quot;age&quot;: 28}
]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;겉보기에는 복잡해 보이지만 구조를 나눠보면 어렵지 않습니다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;users는 리스트다.
리스트 안에는 딕셔너리가 들어 있다.
각 딕셔너리는 한 사람의 정보를 나타낸다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값을 하나 꺼내보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;print(users[0][&quot;name&quot;])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;민수&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해석 순서는 이렇습니다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;users[0]          &amp;rarr; 첫 번째 사람 정보
users[0][&quot;name&quot;]  &amp;rarr; 첫 번째 사람의 name 값&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조는 나중에 웹 데이터, JSON, API 응답을 다룰 때 자주 나옵니다.&lt;br /&gt;따라서 Python을 계속 공부할 생각이라면 &lt;code&gt;list&lt;/code&gt;와 &lt;code&gt;dict&lt;/code&gt;는 꼭 익숙해져야 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;수정 가능한 자료형과 수정 불가능한 자료형&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 자료형을 배울 때 중요한 기준이 하나 더 있습니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;객체의 내용을 바꿀 수 있는가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 기준으로 자료형을 나누면 다음과 같습니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;자료형 예시&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;수정 불가능&lt;/td&gt;
&lt;td&gt;&lt;code&gt;int&lt;/code&gt;, &lt;code&gt;float&lt;/code&gt;, &lt;code&gt;str&lt;/code&gt;, &lt;code&gt;tuple&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;객체 자체를 바꾸기보다 새 객체에 이름을 다시 연결&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;수정 가능&lt;/td&gt;
&lt;td&gt;&lt;code&gt;list&lt;/code&gt;, &lt;code&gt;dict&lt;/code&gt;, &lt;code&gt;set&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;객체 안에 들어 있는 항목을 바꿀 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 문자열은 수정 불가능한 자료형입니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;text = &quot;Python&quot;
text = &quot;Python Study&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 문자열 자체를 고친다기보다, &lt;code&gt;text&lt;/code&gt;라는 이름을 새 문자열 객체에 다시 연결하는 방식으로 이해하는 편이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 리스트는 내부 값을 바꿀 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;numbers = [1, 2, 3]
numbers[0] = 100

print(numbers)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[100, 2, 3]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 차이는 나중에 매우 중요해집니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;초보자가 자주 틀리는 부분: 리스트 복사&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬을 처음 배울 때 많이 헷갈리는 예제가 있습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;a = [1, 2, 3]
b = a

b.append(4)

print(a)
print(b)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보자는 이렇게 예상할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;a는 [1, 2, 3]
b는 [1, 2, 3, 4]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[1, 2, 3, 4]
[1, 2, 3, 4]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 그럴까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;b = a&lt;/code&gt;는 리스트를 새로 복사한 것이 아닙니다.&lt;br /&gt;&lt;code&gt;b&lt;/code&gt;라는 이름도 &lt;code&gt;a&lt;/code&gt;가 가리키던 같은 리스트 객체를 가리키게 만든 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 튜토리얼에서도 단순 대입은 데이터를 복사하지 않는다고 설명합니다.&lt;br /&gt;리스트를 복사하려면 아래처럼 할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;a = [1, 2, 3]
b = a.copy()

b.append(4)

print(a)
print(b)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;[1, 2, 3]
[1, 2, 3, 4]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보자는 이 부분을 꼭 기억해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;리스트를 복사하고 싶다면 b = a만 쓰면 안 된다.&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;자료형 변환: &lt;code&gt;int()&lt;/code&gt;, &lt;code&gt;str()&lt;/code&gt;, &lt;code&gt;float()&lt;/code&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코딩을 하다 보면 자료형을 바꿔야 할 때가 많습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 사용자에게 입력받은 값은 보통 문자열로 들어옵니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;age = &quot;25&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 값을 숫자로 계산하려면 &lt;code&gt;int()&lt;/code&gt;를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;age = &quot;25&quot;
next_year_age = int(age) + 1

print(next_year_age)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;26&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자주 쓰는 변환은 다음과 같습니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;함수&lt;/th&gt;
&lt;th&gt;뜻&lt;/th&gt;
&lt;th&gt;예시&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;int()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;정수로 변환&lt;/td&gt;
&lt;td&gt;&lt;code&gt;int(&quot;10&quot;)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;float()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;실수로 변환&lt;/td&gt;
&lt;td&gt;&lt;code&gt;float(&quot;3.14&quot;)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;str()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;문자열로 변환&lt;/td&gt;
&lt;td&gt;&lt;code&gt;str(100)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;list()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;리스트로 변환&lt;/td&gt;
&lt;td&gt;&lt;code&gt;list(&quot;abc&quot;)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 문자열을 리스트로 바꾸면 글자 단위로 나뉩니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;letters = list(&quot;Python&quot;)

print(letters)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;scheme&quot;&gt;&lt;code&gt;['P', 'y', 't', 'h', 'o', 'n']&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의할 점도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;number = int(&quot;abc&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 오류가 납니다.&lt;br /&gt;&lt;code&gt;&quot;abc&quot;&lt;/code&gt;는 숫자로 바꿀 수 없는 문자열이기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보자는 자료형 변환을 할 때 이렇게 생각하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;겉보기만 숫자인가?
정말 숫자로 바꿀 수 있는 형태인가?&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;&lt;b&gt;=&lt;/b&gt;&lt;/code&gt;&lt;b&gt;와 &lt;code&gt;==&lt;/code&gt;를 구분해야 한다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수를 배울 때 &lt;code&gt;=&lt;/code&gt;와 &lt;code&gt;==&lt;/code&gt;도 자주 헷갈립니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;기호&lt;/th&gt;
&lt;th&gt;의미&lt;/th&gt;
&lt;th&gt;예시&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;=&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;이름을 값에 연결&lt;/td&gt;
&lt;td&gt;&lt;code&gt;age = 25&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;==&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;두 값이 같은지 비교&lt;/td&gt;
&lt;td&gt;&lt;code&gt;age == 25&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래 코드는 이름을 값에 연결하는 코드입니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;age = 25&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드는 값이 같은지 확인하는 코드입니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;age = 25

print(age == 25)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;True&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건문에서는 &lt;code&gt;==&lt;/code&gt;를 자주 씁니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;password = &quot;1234&quot;

if password == &quot;1234&quot;:
    print(&quot;로그인 성공&quot;)
else:
    print(&quot;로그인 실패&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;로그인 성공&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보자는 이렇게 기억하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;asciidoc&quot;&gt;&lt;code&gt;=  : 이름을 값에 연결한다
== : 같은지 물어본다&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;실제 예제로 변수와 자료형 이해하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 간단한 예제를 하나 만들어보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;product_name = &quot;무선 마우스&quot;
price = 25000
delivery_fee = 3000
is_member = True

total_price = price + delivery_fee

print(f&quot;상품명: {product_name}&quot;)
print(f&quot;상품 가격: {price}원&quot;)
print(f&quot;배송비: {delivery_fee}원&quot;)
print(f&quot;총 결제 금액: {total_price}원&quot;)
print(f&quot;회원 여부: {is_member}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;상품명: 무선 마우스
상품 가격: 25000원
배송비: 3000원
총 결제 금액: 28000원
회원 여부: True&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예제에는 여러 자료형이 들어 있습니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style4&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;이름&lt;/th&gt;
&lt;th&gt;값의 자료형&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;product_name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;str&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;상품명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;price&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;int&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;상품 가격&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;delivery_fee&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;int&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;배송비&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;is_member&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bool&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;회원 여부&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;total_price&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;int&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;계산 결과&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 실제 상황과 연결하면 변수와 자료형이 훨씬 쉽게 이해됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;ChatGPT로 변수와 자료형을 공부할 때 좋은 질문&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChatGPT를 활용한다면 단순히 &amp;ldquo;자료형 알려줘&amp;rdquo;라고 묻기보다, 조건을 구체적으로 주는 편이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래처럼 질문해보세요.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;나는 Python을 처음 배우는 비전공자야.
변수와 자료형을 쇼핑몰 예제로 설명해줘.
int, str, bool, list, dict가 각각 하나씩 들어간 예제 코드를 만들어줘.
각 줄마다 주석을 달아줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류가 났을 때는 이렇게 질문하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;아래 Python 코드에서 TypeError가 났어.
초보자 기준으로 원인을 설명하고, 수정된 코드를 보여줘.

[코드]
...
[오류 메시지]
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복습할 때는 문제를 만들어달라고 요청하는 것도 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;Python 변수와 자료형을 연습할 수 있는 초보자용 문제 5개를 내줘.
정답은 바로 보여주지 말고, 내가 답을 쓰면 채점해줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 방식으로 공부하면 답을 그냥 읽는 것보다 기억에 오래 남습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;연습문제&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 문제는 직접 풀어보는 것을 추천합니다.&lt;br /&gt;정답을 보기 전에 코드를 먼저 실행해보세요.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;문제 1. 자료형 확인하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 값들의 자료형을 &lt;code&gt;type()&lt;/code&gt;으로 출력해보세요.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;title = &quot;Python 기초&quot;
views = 150
rating = 4.8
is_published = True
tags = [&quot;Python&quot;, &quot;입문&quot;, &quot;자료형&quot;]
empty_value = None&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;문제 2. 문자열과 숫자 함께 출력하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 이름과 값을 사용해 다음 문장을 출력해보세요.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;name = &quot;민수&quot;
age = 25&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력 결과:&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;민수님의 나이는 25살입니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;힌트: &lt;code&gt;f-string&lt;/code&gt;을 사용해보세요.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;문제 3. 리스트에 값 추가하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 리스트에 &lt;code&gt;&quot;Docker&quot;&lt;/code&gt;를 추가한 뒤 출력해보세요.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;skills = [&quot;Python&quot;, &quot;Git&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;문제 4. 딕셔너리에서 값 꺼내기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 딕셔너리에서 &lt;code&gt;title&lt;/code&gt;과 &lt;code&gt;views&lt;/code&gt;를 출력해보세요.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;post = {
    &quot;title&quot;: &quot;파이썬 변수와 자료형&quot;,
    &quot;views&quot;: 100
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;문제 5. 리스트 복사 확인하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드를 실행하면 어떤 결과가 나올지 먼저 예상해보세요.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;a = [1, 2, 3]
b = a
b.append(4)

print(a)
print(b)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음 &lt;code&gt;b = a.copy()&lt;/code&gt;로 바꿔서 다시 실행해보세요.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;정리: 변수와 자료형은 Python의 출발점이다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 변수와 자료형은 단순한 기초 문법처럼 보이지만, 이후 모든 코드의 출발점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글의 핵심은 세 가지입니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. 변수는 값에 붙이는 이름표에 가깝다.
2. 자료형은 값 객체가 어떤 종류인지 알려준다.
3. 자료형에 따라 할 수 있는 일이 달라진다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 &lt;code&gt;int&lt;/code&gt;, &lt;code&gt;float&lt;/code&gt;, &lt;code&gt;str&lt;/code&gt;, &lt;code&gt;bool&lt;/code&gt;, &lt;code&gt;None&lt;/code&gt;, &lt;code&gt;list&lt;/code&gt;, &lt;code&gt;dict&lt;/code&gt; 정도만 확실히 익혀도 충분합니다.&lt;br /&gt;그다음 조건문, 반복문, 함수로 넘어가면 Python 코드가 훨씬 자연스럽게 읽히기 시작합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 단계로는 &lt;code&gt;for&lt;/code&gt; 반복문과 &lt;code&gt;while&lt;/code&gt; 반복문을 배우는 것이 좋습니다.&lt;br /&gt;리스트와 딕셔너리를 반복문과 함께 쓰면 실제 자동화 예제를 만들 수 있기 때문입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;FAQ&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Q1. 파이썬 변수는 꼭 자료형을 미리 정해야 하나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아니요. 파이썬은 변수를 만들 때 &lt;code&gt;int age = 25&lt;/code&gt;처럼 자료형을 미리 쓰지 않습니다. &lt;code&gt;age = 25&lt;/code&gt;라고 쓰면 &lt;code&gt;age&lt;/code&gt;라는 이름이 정수 객체 &lt;code&gt;25&lt;/code&gt;에 연결됩니다. 이후 &lt;code&gt;age = &quot;25&quot;&lt;/code&gt;처럼 다시 대입하면 같은 이름이 문자열 객체 &lt;code&gt;&quot;25&quot;&lt;/code&gt;에 새로 연결될 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Q2. 숫자처럼 생긴 &lt;code&gt;&quot;100&quot;&lt;/code&gt;은 숫자인가요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아닙니다. 따옴표 안에 있으면 문자열입니다. &lt;code&gt;&quot;100&quot;&lt;/code&gt;은 &lt;code&gt;str&lt;/code&gt;이고, &lt;code&gt;100&lt;/code&gt;은 &lt;code&gt;int&lt;/code&gt;입니다. 계산하려면 &lt;code&gt;int(&quot;100&quot;)&lt;/code&gt;처럼 변환해야 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Q3. &lt;code&gt;list&lt;/code&gt;와 &lt;code&gt;tuple&lt;/code&gt;의 가장 큰 차이는 무엇인가요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;list&lt;/code&gt;는 값을 추가하거나 바꿀 수 있고, &lt;code&gt;tuple&lt;/code&gt;은 한 번 만든 뒤 항목을 바꿀 수 없습니다. 초보자는 값이 바뀔 가능성이 있으면 &lt;code&gt;list&lt;/code&gt;, 고정된 순서형 묶음이면 &lt;code&gt;tuple&lt;/code&gt;로 생각하면 됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Q4. &lt;code&gt;dict&lt;/code&gt;는 언제 쓰나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름, 나이, 조회수처럼 항목 이름과 값이 함께 있는 데이터를 표현할 때 씁니다. 사용자 정보, 상품 정보, 블로그 글 정보, API 응답에서 자주 사용합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Q5. &lt;code&gt;type()&lt;/code&gt;은 실무에서도 쓰나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디버깅할 때 사용할 수 있습니다. 다만 실제 코드에서 자료형을 검사할 때는 상황에 따라 &lt;code&gt;isinstance()&lt;/code&gt;를 쓰는 경우도 많습니다. 입문 단계에서는 &lt;code&gt;type()&lt;/code&gt;으로 값의 자료형을 확인하는 연습부터 하면 됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Q6. 변수 이름은 한글로 써도 되나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬에서는 한글 변수명도 동작할 수 있습니다. 다만 예제, 문서, 협업 환경은 대부분 영어 기준이므로 처음에는 영어 변수명에 익숙해지는 편이 좋습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Q7. &lt;code&gt;None&lt;/code&gt;과 &lt;code&gt;NoneType&lt;/code&gt;은 같은 말인가요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 말은 아닙니다. &lt;code&gt;None&lt;/code&gt;은 값의 부재를 나타내는 특별한 값이고, &lt;code&gt;NoneType&lt;/code&gt;은 그 &lt;code&gt;None&lt;/code&gt; 값의 타입입니다. &lt;code&gt;type(None)&lt;/code&gt;을 실행하면 &lt;code&gt;&amp;lt;class 'NoneType'&amp;gt;&lt;/code&gt;이 출력됩니다.&lt;/p&gt;</description>
      <category>개발/python</category>
      <category>chatgpt코딩</category>
      <category>Python</category>
      <category>python자료형</category>
      <category>비전공자코딩</category>
      <category>자동화</category>
      <category>코딩공부</category>
      <category>파이썬</category>
      <category>파이썬기초</category>
      <category>파이썬변수</category>
      <category>파이썬입문</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/3</guid>
      <comments>https://notebase.tistory.com/entry/python-variables-data-types#entry3comment</comments>
      <pubDate>Wed, 20 May 2026 09:31:02 +0900</pubDate>
    </item>
    <item>
      <title>ChatGPT로 Python 공부하는 방법: 비전공자도 따라 하는 파이썬 입문 로드맵</title>
      <link>https://notebase.tistory.com/entry/chatgpt-python-study-guide</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;ChatGPT로&amp;nbsp;Python을&amp;nbsp;공부해도&amp;nbsp;괜찮을까요?&amp;nbsp;2026년&amp;nbsp;5월&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;630&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KY25d/dJMcaa6sb2y/S2tdBxxDqJ6ZpyGAZDDGQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KY25d/dJMcaa6sb2y/S2tdBxxDqJ6ZpyGAZDDGQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KY25d/dJMcaa6sb2y/S2tdBxxDqJ6ZpyGAZDDGQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKY25d%2FdJMcaa6sb2y%2FS2tdBxxDqJ6ZpyGAZDDGQ0%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;1200&quot; height=&quot;630&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;630&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;ChatGPT로 Python 공부를 시작하려는 사람이 많아졌습니다.&lt;br /&gt;예전에는 책이나 강의로 문법을 따라가는 방식이 일반적이었지만, 이제는 모르는 개념을 바로 질문하고, 예제 코드를 만들고, 오류 메시지까지 해석해주는 AI 도구를 함께 쓸 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 ChatGPT만 있으면 Python을 제대로 배울 수 있을까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론부터 말하면, &lt;b&gt;ChatGPT는 좋은 학습 도구가 될 수 있습니다.&lt;/b&gt;&lt;br /&gt;다만 선생님을 완전히 대체한다기보다는, 옆에서 설명을 바꿔주고 예제를 만들어주는 &lt;b&gt;보조 코치&lt;/b&gt;에 가깝습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 2026년 5월 기준으로 ChatGPT를 활용해 Python을 공부하는 방법을 초보자 기준으로 정리해보겠습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2026년 5월 기준 Python 최신 버전은?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2026년 5월 기준 Python 공식 다운로드 페이지에서 확인되는 최신 안정 버전은 &lt;b&gt;Python 3.14.5&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 초보자 입장에서는 버전 숫자보다 중요한 것이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;너무 오래된 Python 2.x 자료를 따라 하지 않는 것&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요즘 Python 입문 자료는 대부분 Python 3 기준입니다.&lt;br /&gt;따라서 처음 설치할 때는 공식 사이트에서 제공하는 최신 Python 3 버전을 설치하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음&amp;nbsp;설치하는&amp;nbsp;사람이라면&amp;nbsp;운영체제에&amp;nbsp;맞게&amp;nbsp;Python&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;ldquo;설치 후 터미널에서 실행 확인&amp;rdquo;까지 하는 것이 중요합니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;python --version&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 환경에 따라 아래 명령어를 사용할 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;python3 --version&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상적으로 설치되었다면 다음과 비슷한 결과가 나옵니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Python 3.14.5&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버전 숫자가 조금 달라도 괜찮습니다.&lt;br /&gt;Python 3.12, 3.13, 3.14처럼 비교적 최근 버전이라면 입문 학습에는 큰 문제가 없습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;ChatGPT로 Python을 공부해도 괜찮을까?&lt;/b&gt;&lt;/h2&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;Python 공식 사이트에서도 Python은 초보자가 배우고 사용하기 쉬운 언어라고 설명합니다. 그래서 프로그래밍을 처음 배우는 사람이 시작하기 좋은 언어로 자주 추천됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChatGPT를 함께 쓰면 다음과 같은 부분에서 도움을 받을 수 있습니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;상황&lt;/th&gt;
&lt;th&gt;ChatGPT 활용법&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;개념이 이해되지 않을 때&lt;/td&gt;
&lt;td&gt;쉬운 비유로 다시 설명해달라고 요청&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;예제가 부족할 때&lt;/td&gt;
&lt;td&gt;비슷한 예제를 3개 더 만들어달라고 요청&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;오류가 났을 때&lt;/td&gt;
&lt;td&gt;에러 메시지를 붙여넣고 원인 분석 요청&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;코드가 길어졌을 때&lt;/td&gt;
&lt;td&gt;한 줄씩 주석을 달아달라고 요청&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;공부 순서를 모를 때&lt;/td&gt;
&lt;td&gt;4주 학습 계획을 만들어달라고 요청&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;ChatGPT가 항상 정확한 답을 주는 것은 아닙니다.&lt;br /&gt;특히 라이브러리 버전, 설치 명령어, 최신 기능, 운영체제별 차이에서는 틀릴 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 초보자는 ChatGPT 답변을 그대로 외우기보다, &lt;b&gt;직접 실행해보고 결과를 확인하는 방식&lt;/b&gt;으로 공부해야 합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;ChatGPT로 Python 공부하는 5단계&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cci7YB/dJMcagS7DvM/d31W4H0xAHKDVB3O8gQOo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cci7YB/dJMcagS7DvM/d31W4H0xAHKDVB3O8gQOo0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cci7YB/dJMcagS7DvM/d31W4H0xAHKDVB3O8gQOo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcci7YB%2FdJMcagS7DvM%2Fd31W4H0xAHKDVB3O8gQOo0%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;1200&quot; height=&quot;800&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1단계: Python이 어디에 쓰이는지 먼저 이해하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 문법을 외우려고 하면 금방 지칩니다.&lt;br /&gt;먼저 Python으로 무엇을 할 수 있는지 이해하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;엑셀 파일 정리&lt;/li&gt;
&lt;li&gt;반복 업무 자동화&lt;/li&gt;
&lt;li&gt;웹 데이터 수집&lt;/li&gt;
&lt;li&gt;간단한 프로그램 만들기&lt;/li&gt;
&lt;li&gt;데이터 분석&lt;/li&gt;
&lt;li&gt;AI, 머신러닝 기초 학습&lt;/li&gt;
&lt;li&gt;블로그 운영 자동화&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 배우는 사람이라면 &amp;ldquo;웹사이트 만들기&amp;rdquo;보다 &lt;b&gt;파일 정리, 엑셀 자동화, 텍스트 정리&lt;/b&gt; 같은 작은 작업부터 시작하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChatGPT에는 이렇게 물어볼 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;나는 코딩을 처음 배우는 비전공자야.
Python으로 실제 생활에서 할 수 있는 쉬운 예제 10가지를 알려줘.
난이도도 함께 표시해줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 질문은 단순히 문법을 배우는 것보다 훨씬 동기부여가 됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2단계: 설치와 실행 환경부터 확인하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python 공부를 시작할 때 가장 많이 막히는 부분은 의외로 문법이 아닙니다.&lt;br /&gt;설치와 실행 환경입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보자는 다음 세 가지만 확인하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. Python이 설치되어 있는가?
2. 터미널에서 python 명령어가 실행되는가?
3. VS Code 같은 편집기에서 코드를 실행할 수 있는가?&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 ChatGPT에는 이렇게 질문하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;Mac에서 Python을 처음 설치하려고 해.
공식 사이트 기준으로 설치하는 방법을 초보자도 따라 할 수 있게 단계별로 알려줘.
설치 후 확인 명령어도 알려줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Windows라면 이렇게 바꾸면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;Windows 11에서 Python을 처음 설치하려고 해.
PATH 설정이 뭔지도 모르는 초보자 기준으로 설명해줘.
설치 후 python --version을 확인하는 방법까지 알려줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의할 점은 운영체제를 꼭 말해야 한다는 것입니다.&lt;br /&gt;Windows, Mac, Linux는 설치 과정이 다릅니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3단계: 변수, 조건문, 반복문만 먼저 익히기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python을 처음 배울 때 모든 문법을 한 번에 외우려고 하면 어렵습니다.&lt;br /&gt;처음에는 아래 세 가지만 집중해도 됩니다.&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;변수: 값을 담는 이름
조건문: 상황에 따라 다른 코드를 실행하는 방법
반복문: 같은 작업을 여러 번 실행하는 방법&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 변수는 이렇게 이해할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;name = &quot;민수&quot;
age = 25

print(name)
print(age)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드는 &lt;code&gt;name&lt;/code&gt;이라는 이름에 &amp;ldquo;민수&amp;rdquo;를 담고, &lt;code&gt;age&lt;/code&gt;라는 이름에 25를 담은 뒤 출력합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건문은 이렇게 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;age = 20

if age &amp;gt;= 20:
    print(&quot;성인입니다.&quot;)
else:
    print(&quot;미성년자입니다.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복문은 같은 일을 여러 번 할 때 사용합니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;for i in range(5):
    print(&quot;Python 공부 중입니다.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보자는 이 코드를 외우려고 하기보다, ChatGPT에게 설명을 요청하는 것이 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;아래 Python 코드를 한 줄씩 초보자 기준으로 설명해줘.
비유를 들어서 설명해줘.

for i in range(5):
    print(&quot;Python 공부 중입니다.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 질문하면 단순한 문법 설명보다 훨씬 이해하기 쉽습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4단계: 짧은 자동화 예제를 따라 하기&lt;/b&gt;&lt;/h3&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;예를 들어 폴더 안의 파일명을 정리하는 작업, 텍스트에서 특정 단어를 세는 작업, 간단한 계산기를 만드는 작업이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 이런 예제로 시작할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;text = &quot;Python is easy. Python is useful.&quot;

count = text.count(&quot;Python&quot;)

print(&quot;Python이라는 단어는&quot;, count, &quot;번 나옵니다.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Python이라는 단어는 2 번 나옵니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정도 예제는 짧지만, 변수와 문자열 처리, 출력 개념이 들어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChatGPT에는 이렇게 질문하면 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;Python 초보자가 따라 할 수 있는 10줄 이하의 자동화 예제를 만들어줘.
변수, 조건문, 반복문 중 하나 이상이 들어가게 해줘.
그리고 코드 아래에 한 줄씩 설명을 붙여줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 큰 프로그램을 만들 필요는 없습니다.&lt;br /&gt;작은 코드를 여러 번 실행해보는 것이 더 중요합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5단계: 오류 메시지를 ChatGPT로 해석하기&lt;/b&gt;&lt;/h3&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;예를 들어 이런 오류가 나왔다고 해보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;xquery&quot;&gt;&lt;code&gt;NameError: name 'pritn' is not defined&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보자는 이 메시지를 보고 당황할 수 있습니다.&lt;br /&gt;하지만 실제 원인은 단순합니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;pritn(&quot;Hello&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;print&lt;/code&gt;를 &lt;code&gt;pritn&lt;/code&gt;으로 잘못 입력한 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴 때 ChatGPT에는 이렇게 질문하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;xquery&quot;&gt;&lt;code&gt;Python에서 아래 오류가 나왔어.
초보자 기준으로 원인과 해결 방법을 설명해줘.

NameError: name 'pritn' is not defined&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가능하면 오류 메시지만 보내지 말고, &lt;b&gt;실행한 코드와 오류 메시지를 함께 보내는 것&lt;/b&gt;이 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;아래 Python 코드를 실행했더니 오류가 났어.
1. 오류 원인
2. 수정된 코드
3. 앞으로 같은 실수를 피하는 방법
이렇게 나눠서 설명해줘.

[내 코드]
...

[오류 메시지]
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChatGPT의 장점은 오류를 무섭지 않게 만들어준다는 점입니다.&lt;br /&gt;초보자는 이 과정에서 &amp;ldquo;에러 메시지를 읽는 습관&amp;rdquo;을 만들 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;ChatGPT에게 이렇게 질문하면 공부가 잘 된다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChatGPT를 코딩 공부에 잘 활용하려면 질문 방식이 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나쁜 질문은 이렇게 너무 짧습니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;파이썬 알려줘&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 물으면 답변이 넓고 뻔해질 가능성이 큽니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 질문은 조건이 구체적입니다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;나는 Python을 처음 배우는 비전공자야.
변수, 조건문, 반복문을 각각 5줄 이하 예제로 설명해줘.
각 코드 아래에는 초보자용 설명을 붙여줘.
마지막에는 내가 직접 풀 수 있는 연습문제 3개를 내줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 이런 식도 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;prolog&quot;&gt;&lt;code&gt;아래 Python 코드를 초보자 기준으로 설명해줘.
전문 용어는 쉽게 풀어주고, 각 줄마다 주석을 달아줘.

[코드 붙여넣기]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공부용으로는 다음 형식을 기억하면 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;&quot;&gt;&lt;code&gt;내 수준 + 원하는 주제 + 예제 조건 + 설명 방식&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어:&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;나는 코딩을 처음 배우는 사람이야.
Python의 for 반복문을 배우고 싶어.
엑셀 업무 자동화와 연결되는 쉬운 예제로 설명해줘.
코드는 10줄 이하로 작성해줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 질문하면 ChatGPT 답변의 품질이 훨씬 좋아집니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;ChatGPT 답변을 그대로 믿으면 안 되는 경우&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChatGPT가 코딩 공부에 도움이 되는 것은 맞지만, 그대로 믿으면 안 되는 경우도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 아래 상황에서는 반드시 확인이 필요합니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;상황&lt;/th&gt;
&lt;th&gt;주의할 점&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;설치 명령어&lt;/td&gt;
&lt;td&gt;운영체제마다 다를 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;라이브러리 사용법&lt;/td&gt;
&lt;td&gt;버전에 따라 코드가 달라질 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;오래된 블로그 코드&lt;/td&gt;
&lt;td&gt;현재 버전에서 작동하지 않을 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;보안 관련 코드&lt;/td&gt;
&lt;td&gt;민감한 정보가 노출될 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;자동화 코드&lt;/td&gt;
&lt;td&gt;파일 삭제, 덮어쓰기 위험이 있을 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 파일을 삭제하거나 이름을 바꾸는 코드는 반드시 테스트 폴더에서 먼저 실행해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;haskell&quot;&gt;&lt;code&gt;import os

# 실제 중요한 폴더에서 바로 실행하지 말 것&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보자는 ChatGPT에게 코드 작성을 요청할 때 이렇게 덧붙이는 것이 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;실제 파일을 삭제하거나 덮어쓰지 않는 안전한 예제로 만들어줘.
테스트용 폴더에서만 실행하는 방식으로 설명해줘.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 한 문장만 추가해도 위험한 예제를 줄일 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;ChatGPT의 Python 실행 기능도 활용할 수 있다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2026년 기준 ChatGPT에는 코딩 학습에 도움이 되는 기능이 더 늘었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenAI 도움말에 따르면 ChatGPT의 데이터 분석 기능은 일부 작업에서 Python 코드를 실행해 파일, 표, CSV 같은 데이터를 분석할 수 있습니다. 또한 Canvas는 글쓰기와 코딩 프로젝트를 편집하고 수정하는 인터페이스로 제공됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 단순히 &amp;ldquo;코드를 물어보는 도구&amp;rdquo;를 넘어, 일부 환경에서는 코드를 실행하고 결과를 확인하는 방식으로도 활용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 초보자는 처음부터 이 기능에만 의존하지 않는 것이 좋습니다.&lt;br /&gt;내 컴퓨터에 Python을 설치하고, 직접 실행해보는 경험도 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추천 방식은 이렇습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;1. ChatGPT로 개념 설명 듣기
2. 내 컴퓨터에서 직접 코드 실행하기
3. 오류가 나면 오류 메시지를 ChatGPT에 질문하기
4. 수정된 코드를 다시 직접 실행하기&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정을 반복하면 단순히 답을 복사하는 것이 아니라, 코드가 어떻게 작동하는지 이해할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Python 공부 후 이어서 배우면 좋은 기술&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python 기초를 어느 정도 익혔다면 다음 단계로 넘어갈 수 있습니다.&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;다음 주제&lt;/th&gt;
&lt;th&gt;배우면 좋은 이유&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Git / GitHub&lt;/td&gt;
&lt;td&gt;코드 저장과 기록 관리에 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VS Code&lt;/td&gt;
&lt;td&gt;Python 코드를 편하게 작성하는 편집기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;가상환경&lt;/td&gt;
&lt;td&gt;프로젝트별 라이브러리 관리를 위해 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pandas&lt;/td&gt;
&lt;td&gt;엑셀, CSV 데이터 정리에 유용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;requests&lt;/td&gt;
&lt;td&gt;웹 데이터 요청 기초를 배울 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker&lt;/td&gt;
&lt;td&gt;실행 환경을 일정하게 관리할 때 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초보자에게 가장 추천하는 순서는 이렇습니다.&lt;/p&gt;
&lt;pre class=&quot;armasm&quot;&gt;&lt;code&gt;Python 기초
&amp;rarr; VS Code 사용법
&amp;rarr; GitHub 기초
&amp;rarr; pandas로 엑셀 자동화
&amp;rarr; Docker 기초&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음부터 Docker나 서버 배포까지 가려고 하면 어렵습니다.&lt;br /&gt;먼저 Python으로 작은 문제를 해결하는 경험을 만드는 것이 좋습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;정리: ChatGPT는 Python 공부의 지름길이 아니라 보조 코치다&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChatGPT로 Python을 공부하는 것은 충분히 좋은 방법입니다.&lt;br /&gt;특히 비전공자나 초보자에게는 모르는 개념을 바로 물어볼 수 있다는 점이 큰 장점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 ChatGPT가 모든 답을 정확하게 주는 것은 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 가장 좋은 공부 방식은 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;erlang&quot;&gt;&lt;code&gt;설명은 ChatGPT에게 듣고,
코드는 직접 실행하고,
오류는 다시 ChatGPT에게 질문하고,
결과는 스스로 확인한다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python은 처음 배울 때 어렵게 느껴질 수 있지만, 작은 예제를 반복하면 생각보다 빠르게 익숙해집니다.&lt;/p&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;ChatGPT를 잘 활용하면 혼자 공부할 때 막히는 시간을 줄일 수 있습니다.&lt;br /&gt;중요한 것은 답을 복사하는 것이 아니라, 왜 그렇게 작동하는지 계속 질문하는 것입니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;FAQ&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Q1. ChatGPT만으로 Python을 배워도 되나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기초 학습은 가능합니다. 다만 ChatGPT 답변을 그대로 믿기보다, 직접 코드를 실행하면서 확인해야 합니다. 설치 방법이나 라이브러리 사용법은 최신 공식 문서도 함께 확인하는 것이 좋습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Q2. Python을 처음 배우면 어떤 버전을 설치해야 하나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2026년 5월 기준 Python 공식 사이트의 최신 안정 버전은 Python 3.14.5입니다. 초보자는 공식 사이트에서 제공하는 최신 Python 3 버전을 설치하면 됩니다. 다만 강의나 책이 특정 버전을 기준으로 한다면 그 버전에 맞춰도 됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Q3. 코딩을 전혀 몰라도 ChatGPT로 공부할 수 있나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가능합니다. 대신 처음부터 어려운 프로젝트를 만들기보다 변수, 조건문, 반복문, 함수처럼 기본 개념부터 차근차근 공부하는 것이 좋습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Q4. ChatGPT가 만들어준 코드는 항상 맞나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;항상 맞지는 않습니다. 특히 최신 라이브러리, 운영체제별 설치 방법, 파일 삭제나 자동화 코드에서는 주의가 필요합니다. 반드시 테스트 환경에서 먼저 실행해보는 것이 좋습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Q5. Python 다음에는 무엇을 배우면 좋나요?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목적에 따라 다릅니다. 업무 자동화가 목적이라면 pandas와 엑셀 자동화를 배우는 것이 좋고, 개발자로 확장하고 싶다면 Git, GitHub, Docker를 이어서 배우는 것이 좋습니다.&lt;/p&gt;</description>
      <category>개발/python</category>
      <category>AI코딩</category>
      <category>chatGPT</category>
      <category>Python</category>
      <category>Python공부법</category>
      <category>비전공자코딩</category>
      <category>자동화</category>
      <category>코딩공부</category>
      <category>파이썬</category>
      <category>파이썬입문</category>
      <author>notebase</author>
      <guid isPermaLink="true">https://notebase.tistory.com/2</guid>
      <comments>https://notebase.tistory.com/entry/chatgpt-python-study-guide#entry2comment</comments>
      <pubDate>Wed, 20 May 2026 08:55:01 +0900</pubDate>
    </item>
  </channel>
</rss>