<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>프로그래밍</title>
    <link>https://dev-9rm.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Sun, 12 Apr 2026 15:14:05 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>시케</managingEditor>
    <image>
      <title>프로그래밍</title>
      <url>https://tistory1.daumcdn.net/tistory/4659248/attach/5a0eebe5c2014048a70e5f467c1d77a7</url>
      <link>https://dev-9rm.tistory.com</link>
    </image>
    <item>
      <title>[Docker] Docker 파일 활용하여 배포하기(React + Django + PostgreSQL)</title>
      <link>https://dev-9rm.tistory.com/248</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;Docker를 활용하여 소스코드가 변경되어도 쉽게 배포가 가능하도록 해보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Docker가 기본적으로 설치가 되어있다고 가정하겠다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1748221746254&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 오늘 배포할 환경
Front: React
Backend: Django
DB: PostgreSQL&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1748224185876&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 소스코드 트리 구조
TestWrapper
├── docker-compose.yml
├── front
│&amp;nbsp;&amp;nbsp; ├── build
│&amp;nbsp;&amp;nbsp; ├── dockerfile
│&amp;nbsp;&amp;nbsp; ├── node_modules
│&amp;nbsp;&amp;nbsp; ├── package.json
│&amp;nbsp;&amp;nbsp; ├── package-lock.json
│&amp;nbsp;&amp;nbsp; ├── patches
│&amp;nbsp;&amp;nbsp; ├── poo.txt
│&amp;nbsp;&amp;nbsp; ├── public
│&amp;nbsp;&amp;nbsp; ├── README.md
│&amp;nbsp;&amp;nbsp; ├── src
│&amp;nbsp;&amp;nbsp; ├── tailwind.config.js
│&amp;nbsp;&amp;nbsp; └── tsconfig.json
├── package-lock.json
└── test
    ├── backend
    │&amp;nbsp;&amp;nbsp; ├── dockerfile
    │&amp;nbsp;&amp;nbsp; ├── config
    │&amp;nbsp;&amp;nbsp; ├── manage.py
    │&amp;nbsp;&amp;nbsp; ├── requirements.txt
    │&amp;nbsp;&amp;nbsp; ├── uploaded_files
    │&amp;nbsp;&amp;nbsp; ├── user_info
    │&amp;nbsp;&amp;nbsp; └── 각 app 들...
    └── README.md&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; Dockerfile &lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Dockerfile은 컨테이너를 만들기 위한 이미지의 설계서이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;각각 Front와 Backend의 컨테이너를 Dockerfile로 만들것이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;i&gt;&lt;b&gt;dockerfile(Front)&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748222185354&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1. Node.js를 사용해 React 앱을 빌드
FROM node:18 AS builder

# 2. 앱 소스 코드를 컨테이너로 복사하고, 필요한 패키지들을 설치
WORKDIR /app
COPY package*.json ./
RUN npm install

# 3. React 앱을 빌드 ( nginx에서 사용할 수 있도록 )
COPY . .
RUN npm run build

# 4. Nginx를 사용해 빌드된 앱을 서비스하기 위한 이미지 생성
FROM nginx:1.23

# 5. React 앱의 빌드 파일을 Nginx의 기본 경로에 복사
COPY --from=builder /app/build /usr/share/nginx/html

# 7. 새로고침 문제 해결을 위한 Nginx 설정 (기존 default.conf를 수정)
RUN echo &quot;server { \
    listen 80; \
    server_name localhost; \
    root /usr/share/nginx/html; \
    index index.html; \
    location / { \
        try_files \$uri \$uri/ /index.html; \
    } \
}&quot; &amp;gt; /etc/nginx/conf.d/default.conf

# 8. 포트 80을 외부에 노출
EXPOSE 80

# 9. Nginx를 실행
CMD [&quot;nginx&quot;, &quot;-g&quot;, &quot;daemon off;&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;해당 파일을 front-end가 실행되는 위치&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;즉, npm start 하는 위치에 넣어준다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;i&gt;&lt;b&gt;dockerfile(Back)&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748222282058&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1. Python 기반 이미지를 사용합니다.
FROM python:3.10-slim

# 2. PostgreSQL 클라이언트 라이브러리 설치 ( GCC 포함 )
RUN apt-get update &amp;amp;&amp;amp; \
    apt-get install -y gcc libpq-dev python3-dev &amp;amp;&amp;amp; \
    rm -rf /var/lib/apt/lists/*

# 3. 필수 패키지를 설치합니다.
WORKDIR /app

# pip 최신 버전으로 업그레이드 후 requirements.txt 복사 및 설치
RUN pip install --upgrade pip
COPY requirements.txt .
RUN pip install -r requirements.txt

# 4. 소스 코드를 복사합니다.
COPY . .

EXPOSE 8001
CMD [&quot;python&quot;, &quot;manage.py&quot;, &quot;runserver&quot;, &quot;0.0.0.0:8001&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;해당 파일을 back-end가 실행되는 위치&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;즉, runserver를 하는 위치에 넣어준다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;docker-compose.yml&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;docker-compose.yml&lt;/b&gt;은 여러 개의 컨테이너를 한번에 정의하고 실행하기 위한 설정 파일이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;즉, 여러 Dockerfile에서 만들어진 컨테이너들을 묶어서 한꺼번에 구성하고 실행할 수 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;우린 front-end와 back-end의 컨테이너를 만드는 dockerfile을 작성했으므로&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;두 컨테이너를 묶어서 한번에 실행시킬 docker-compose.yml 파일을 작성해보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;docker-compose.yml&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748222536561&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;version: '3.9'

services:
  frontend:
    build:
      context: ./front
      dockerfile: Dockerfile
    ports:
      - &quot;3002:80&quot;  # React 앱에 대해 80번 포트를 외부에 노출
    networks:
      - app-network

  backend:
    build:
      context: ./test/backend
      dockerfile: Dockerfile
    ports:
      - &quot;8001:8001&quot;  # Django 앱에 대해 5123번 포트를 외부에 노출
    networks:
      - app-network
    volumes:
      - ./test/backend/uploaded_files:/app/uploaded_files
      - ./test/backend/user_info:/app/user_info

networks:
  app-network:
    driver: bridge&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;내가 미리 만들어둔 dockerfile을 명시하고 어떤 포트를 각 컨테이너에 노출할지 작성한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;또한 볼륨마운트를 통해 소스코드가 변경되어 재배포하더라도 날라가지않고 유지될 폴더를 지정했다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;원래 컨테이너가 내려갔다가 다시 올라가면 사용자가 업로드한 파일, 사용자 프로필 이미지가 삭제될 수 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이를 방지하고 유지하고 싶기에 &lt;b&gt;컨테이너 내부에 있는 특정 폴더&lt;/b&gt;에 모아뒀다가 &lt;b&gt;컨테이너 밖에 있는 폴더&lt;/b&gt;와 &lt;b&gt;볼륨 마운트&lt;/b&gt;를 시켰다&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;[컨테이너 내부 폴더] &amp;lt;--------[볼륨 마운트] &lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;-------&amp;gt;[컨테이너 외부 폴더]&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;어디를 수정해도 다른 폴더에 반영됨!&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;두 컨테이너를 브릿지를 통해 연결한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이제 파일작성은 끝났으며 &lt;span style=&quot;font-size: 1.12em; letter-spacing: 0px;&quot;&gt;docker-compose.yml 파일 위치에서 명령어만 한줄로 서버에 배포 가능하다&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748223403349&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker compose up -d --build&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;재배포시에는 소스코드 변경 후&lt;/p&gt;
&lt;pre id=&quot;code_1748223436554&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker compose down
docker compose up -d --build&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;★네트워크가 꼬일 수 있기 때문에 반드시 down 후에 다시 빌드해야 한다 ★&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이제 다음으로 접속해보면 알맞게 배포된것을 확인할 수 있다&lt;/p&gt;
&lt;pre id=&quot;code_1748223527051&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;front-end
http://서버아이피:3002/

back-end
http://서버아이피:8001/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CI&amp;middot;CD</category>
      <author>시케</author>
      <guid isPermaLink="true">https://dev-9rm.tistory.com/248</guid>
      <comments>https://dev-9rm.tistory.com/248#entry248comment</comments>
      <pubDate>Mon, 26 May 2025 10:57:39 +0900</pubDate>
    </item>
    <item>
      <title>[Git] 왜 사람들은 git을 어렵다고 느낄까?(Merge와 Conflict)</title>
      <link>https://dev-9rm.tistory.com/247</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;왜&amp;nbsp;사람들은&amp;nbsp;git을&amp;nbsp;어렵다고&amp;nbsp;느낄까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;git을 사용하면서 어렵다고 느끼는 지점은 &quot;merge를 하려고 할때&quot; 찾아오는것 같다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그럼 왜 Merge를 할때면 git의 작동방식을 이해하기가 힘든지&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;도대체 conflict(충돌)은 언제 일어나는지&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;git은 무엇을 기준으로 병합판단을 하는지에 대해서 알아보자&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Merge&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;먼저 Merge는 병합이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;내가 가진 여러개의 브랜치, 거기에 따른 커밋기록을 다시 하나의 브랜치를 합친다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;변경사항을 버전 관리를 하다가 해당 사항들을 합쳐주어서 하나의 버전(소스코드)를 만들 수 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;보통 여러명이 각각 기능 개발을 하고 완성이 되면 개발 브랜치에 합치는 경우를 예시로 들 수 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;브랜치 구조&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1747707546410&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        C  &amp;larr; 병합 대상 (ex: feat_a)
       /
A &amp;mdash; B
       \
        D  &amp;larr; 현재 브랜치 (ex: main)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;main 브랜치&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1747706519415&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;main에서 파생된 feat_a 브랜치&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1747706579383&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;LANGUAGE_CODE = 'kr'	# 변경
TIME_ZONE = 'UTC'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;feat_a -&amp;gt; main 병합 결과&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1747706617599&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;LANGUAGE_CODE = 'kr'	# 변경 적용
TIME_ZONE = None&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이런 간단한 경우는 쉽게 이해할 수 있을것이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;하지만 브랜치가 많아지고 Conflict가 난다면 복잡해진다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;conflict&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Conflict는 충돌이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;git이 자동으로 변경사항을 병합하지 못했을 경우 발생한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;주로 같은 지점을 변경할 경우 발생한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; main 브랜치 &lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1747706899679&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;main에서 파생된 feat_a 브랜치&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1747706971615&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'ABC'	# 변경함&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;main에서 수정&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1747706935613&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;LANGUAGE_CODE = 'en-us'
TIME_ZONE = None	# 변경함&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;수정된 main 브랜치와 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;feat_a&lt;span&gt; 브랜치 병합시도&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;-&amp;gt; 충돌 발생&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;같은 곳을 다른 내용으로 수정했기에 git은 &lt;b&gt;&quot;둘 중에 뭐가 맞아?&quot;&lt;/b&gt;라고 하며 충돌이 발생하게 된다&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;만약 다른 줄을 수정했다면 충돌은 발생하지 않았을 것이다&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;600&quot; data-start=&quot;451&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style5&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;상황&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Git 판단&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;529&quot; data-start=&quot;499&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;513&quot; data-start=&quot;499&quot;&gt;양쪽 모두 변경 안함&lt;/td&gt;
&lt;td data-end=&quot;521&quot; data-start=&quot;513&quot; data-col-size=&quot;sm&quot;&gt;그대로 둠&lt;/td&gt;
&lt;td data-end=&quot;529&quot; data-start=&quot;521&quot; data-col-size=&quot;sm&quot;&gt;✅ 유지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;561&quot; data-start=&quot;530&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;539&quot; data-start=&quot;530&quot;&gt;한쪽만 변경&lt;/td&gt;
&lt;td data-end=&quot;550&quot; data-start=&quot;539&quot; data-col-size=&quot;sm&quot;&gt;변경한 쪽 반영&lt;/td&gt;
&lt;td data-end=&quot;561&quot; data-start=&quot;550&quot; data-col-size=&quot;sm&quot;&gt;✅ 자동 병합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;600&quot; data-start=&quot;562&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;577&quot; data-start=&quot;562&quot;&gt;양쪽 모두 다르게 변경&lt;/td&gt;
&lt;td data-end=&quot;585&quot; data-start=&quot;577&quot; data-col-size=&quot;sm&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;충돌 발생&lt;/span&gt;&lt;/td&gt;
&lt;td data-end=&quot;600&quot; data-start=&quot;585&quot; data-col-size=&quot;sm&quot;&gt;⚠️&lt;span style=&quot;color: #ee2323;&quot;&gt; 수동 해결 필요&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Target 브랜치와 Source 브랜치&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Target&lt;/b&gt; 브랜치는 우리가 흔히 말하는 &lt;b&gt;main&lt;/b&gt;이고&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Source&lt;/b&gt; 브랜치는 &lt;b&gt;feature&lt;/b&gt; 브랜치가 될것이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;병합은 Target 브랜치에 Source 브랜치의 변경사항을 적용한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그럼 두 브랜치가 바뀌면 병합의 결과가 달라질까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;-&amp;gt; 달라지지 않는다&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;git은 변경한쪽의 내용을 따른다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이때 변경한 브랜치가 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Target&lt;span&gt; &lt;/span&gt;&lt;/span&gt;브랜치든 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Source&lt;span&gt; &lt;/span&gt;&lt;/span&gt;브랜치든 &lt;b&gt;상관없이&lt;/b&gt; 변경한쪽의 내용을 무조건 따른다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그렇기에 &lt;b&gt;두 브랜치의 변경사항을 모두 반영&lt;/b&gt;할 수 있는것이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;★Merge는 덮어쓰기가 아니다 &lt;b&gt;★&lt;/b&gt; &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;병합을 하는 원리는 공통 조상을 파악 후 비교하여 변경을 적용하는것이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;만약 덮어쓰기라면 무조건 최신 변경사항을 적용할 것이고&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그럼 충돌이 날 일은 없을 것이다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Merge 예시&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;브랜치가 여러개 파생된 예시를 보고 Merge의 원리를 이해해보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;브랜치 구조&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1747708057043&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;main 브랜치
A &amp;mdash; B &amp;mdash; C &amp;mdash; D(main 브랜치 HEAD)

사용자 E 브랜치 (B에서 시작)
        ↘︎
          E(feature 브랜치)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 사용자가 &lt;b&gt;최신 변경 사항(D)&lt;/b&gt;을 반영하지 않고 &lt;b&gt;과거 시점(B)&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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;main 브랜치의 Head인 D&lt;/b&gt;와 &lt;b&gt;사용자 E 브랜치&lt;/b&gt;를 병합한다면 어떻게 될까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(보통은 과거 시점 브랜치를 보유한 작업자가 pull을 받아 충돌을 해결 후 다시 Merge하는 방법을 추천)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 두 브랜치의 &lt;b&gt;공통 조상&lt;/b&gt;은 &lt;b&gt;main 브랜치의 B&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;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;main 브랜치 B &lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1747708327667&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;A = 1
B = 2
C = 3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;main 브랜치 C &lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1747708352426&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;A = 1          # 변경 없음
B = 2          # 변경 없음
C = 4          # C 변경&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;main 브랜치 D&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1747708374938&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;A = 1          # 변경 없음
B = 3          # B 변경
C = 4          # C는 C 브랜치에서 이미 바뀐 걸 그대로 유지&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;B시점에서 파생된 브랜치 E&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1747708422918&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;A = 0          # A 변경
B = 2          # 변경 없음
C = 3          # 변경 없음 (즉, B의 상태와 동일)
D = 10         # 새 항목 추가&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; 병합 판단 기준 &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;공통 조상(B)에서 &lt;b&gt;무엇이 변경되었는지&lt;/b&gt;를 기준으로 판단한다&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style5&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;항목&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;B(공통 조상)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;D 브랜치&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;E 브랜치&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;병합결과&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1 (변경 X)&lt;/td&gt;
&lt;td&gt;0 (변경 O)&lt;/td&gt;
&lt;td&gt;✅ &lt;b&gt;0&lt;/b&gt; &amp;larr; E만 변경&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3 (변경 O)&lt;/td&gt;
&lt;td&gt;2 (변경 X)&lt;/td&gt;
&lt;td&gt;✅ &lt;b&gt;3&lt;/b&gt; &amp;larr; D만 변경&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;4 (변경 O)&lt;/td&gt;
&lt;td&gt;3 (변경 X)&lt;/td&gt;
&lt;td&gt;✅ &lt;b&gt;4&lt;/b&gt; &amp;larr; D만 변경&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;D&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;10 (추가)&lt;/td&gt;
&lt;td&gt;✅ &lt;b&gt;10&lt;/b&gt; &amp;larr; E만 추가&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;size18&quot;&gt;&lt;b&gt;최종 병합 결과&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1747708509770&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;A = 0      # E에서만 변경 &amp;rarr; 반영됨
B = 3      # D에서만 변경 &amp;rarr; 반영됨
C = 4      # D에서만 변경 &amp;rarr; 반영됨
D = 10     # E에서 새로 추가됨 &amp;rarr; 반영됨&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;만약 단순 덮어쓰기였다면 Source 브랜치인 E 브랜치의 내용이 우선 반영되고&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;E브랜치가 갖고 있지 않은 브랜치 C, D의 변경사항은 날라갔을 것이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;하지만 공통 조상 기준으로 비교하여 반영하므로 브랜치 C, D의 변경사항 또한 반영되었다&lt;/p&gt;
&lt;pre id=&quot;code_1747708744844&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;공통 조상 (Base)         현재 브랜치 (HEAD)            병합 대상 (Other)
       B                        D                          E
    ---------               ---------               	---------
    A = 1                   A = 1           		A = 0   &amp;larr; 변경됨
    B = 2                   B = 3   &amp;larr; 변경됨		B = 2
    C = 3                   C = 4   &amp;larr; 변경됨		C = 3
    (없음)                  (없음)          		D = 10  &amp;larr; 추가됨&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1747708889231&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        A
        │
        B  &amp;larr; 공통 조상 (Merge 기준점)
       / \
      /   \
     C     E
      \   /
       D &amp;larr; 현재 main HEAD (여기에 merge E)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&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;Merge는 덮어쓰기가 아니다&lt;/li&gt;
&lt;li&gt;Merge의 기준은 가장 최신의 공통 조상이다&lt;/li&gt;
&lt;li&gt;같은 곳의 변경이 동시에 일어날 경우 Conflict가 발생한다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CI&amp;middot;CD</category>
      <author>시케</author>
      <guid isPermaLink="true">https://dev-9rm.tistory.com/247</guid>
      <comments>https://dev-9rm.tistory.com/247#entry247comment</comments>
      <pubDate>Tue, 20 May 2025 11:53:33 +0900</pubDate>
    </item>
    <item>
      <title>[cherrypicker] 프로젝트 개요</title>
      <link>https://dev-9rm.tistory.com/246</link>
      <description>&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;cherrypicker&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;사용자가 가지고 있는 옷을 쉽게 정리할 수 있도록 돕는 프로그램이다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;옷의 카테고리별 사이즈, 이미지, 개인 메모 등을 기록하고&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;칸반보드를 통해 한눈에 보기 쉽게 하여 옷을 입거나 정리할때 편의성을 증진시킨다&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;b&gt;Kanban board(칸반보드)&lt;/b&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;사용자는 옷을 입력하거나 수정 및 삭제를 할 수 있으며&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;옷의 카테고리, 사이즈, 메모, 이미지 등을 함께 기록할 수 있다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;해당 옷을 칸반보드에 표시 여부를 결정할 수 있다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;칸반보드는 'Closet', 'Laundary', 'WishList', 'Season-Out' 항목으로 나뉜다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;옷의 상태에 따라 사용자는 칸반보드에서 자유롭게 항목을 변경할 수 있다&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Outfit&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;자신이 가지고 있는 옷을 조합하여 입고 나갈 착장을 기록할 수 있으며&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;다른 사용자에게 공유할 수 있다&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;HashTag&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;옷과 착장은 해시태그를 입력할 수 있다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;해당 해시태그로 옷과 착장을 필터링해서 볼 수 있다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;사용 스택&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;# Front-end&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Node.js v15.0.1&lt;/li&gt;
&lt;li&gt;Next.js v22.12.0 LTS&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;# Back-end&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Python 3.10&lt;/li&gt;
&lt;li&gt;Django REST Framework(APIView, CbV) v4.2 &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;LTS&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;# DB&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;Postgres 16&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>프로젝트/cherrypicker</category>
      <author>시케</author>
      <guid isPermaLink="true">https://dev-9rm.tistory.com/246</guid>
      <comments>https://dev-9rm.tistory.com/246#entry246comment</comments>
      <pubDate>Mon, 6 Jan 2025 11:22:41 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 12789번: 도키도키 간식드리미 (Python)</title>
      <link>https://dev-9rm.tistory.com/245</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;2025.01.03.금&lt;/i&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;12789번: 도키도키 간식드리미&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;921&quot; data-origin-height=&quot;839&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCYx7c/btsLFhA1dpw/iAVI7yJrek73hSnDBNC3RK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCYx7c/btsLFhA1dpw/iAVI7yJrek73hSnDBNC3RK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCYx7c/btsLFhA1dpw/iAVI7yJrek73hSnDBNC3RK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCYx7c%2FbtsLFhA1dpw%2FiAVI7yJrek73hSnDBNC3RK%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;921&quot; height=&quot;839&quot; data-origin-width=&quot;921&quot; data-origin-height=&quot;839&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;930&quot; data-origin-height=&quot;347&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/stThW/btsLD10dsOK/NdrbqtxjhytdOpeZROicQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/stThW/btsLD10dsOK/NdrbqtxjhytdOpeZROicQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/stThW/btsLD10dsOK/NdrbqtxjhytdOpeZROicQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FstThW%2FbtsLD10dsOK%2FNdrbqtxjhytdOpeZROicQk%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;930&quot; height=&quot;347&quot; data-origin-width=&quot;930&quot; data-origin-height=&quot;347&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div id=&quot;code_1736126097266&quot; data-ke-type=&quot;html&quot; data-source=&quot;&amp;lt;a class=&amp;quot;test&amp;quot; href=&amp;quot; https://www.acmicpc.net/problem/12789 &amp;quot; &amp;gt; 12789번 문제 바로가기 &amp;lt;/a&amp;gt;&quot;&gt;&lt;a class=&quot;test&quot; href=&quot; https://www.acmicpc.net/problem/12789 &quot;&gt; 12789번 문제 바로가기 &lt;/a&gt;&lt;/div&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;문제&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;줄 서있는 곳이나 대기열이나 맨 앞 사람만 이동할 수 있고&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;대기열에서 줄 서있는 곳으로 가지 못 한다는 사실만 알면 쉽다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;Queue나 Stack을 사용해도 되지만 단순하게 파이썬의 리스트만 활용하여 풀어보았다&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;풀이방식&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;학생 수와 학생들이 줄을 선 순서를 입력받아 저장한다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;대기공간인 리스트(queue)와 현재 나눠줄 번호표인 변수(num)를 선언한다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;학생들 줄 순서와 대기공간이 있을 경우 다음을 반복한다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;대기줄 맨 앞 학생의 번호표가 현재 번호표와 일치하는 경우&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;대기줄에서 pop()을 통해 제거한다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;※학생줄에서 대기줄로 넘기는 방식이기 때문에 대기줄과 일치하는지 먼저 검사한다 &lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;학생줄 맨 앞 학생의 번호표가 현재 번호표와 일치하는 경우&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;학생줄에서 pop()을 통해 제거&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;학생줄 맨 앞 학생의 번호표가 현재 번호표와 일치하지 않는 경우&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;학생줄에서 대기줄 맨앞으로 보낸다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;만족하는 경우가 없으면&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;즉, 더이상 대기줄이나 학생줄의 맨앞 학생이 번호표와 일치하지 않으며 학생줄이 없을시&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;대기줄로 넘기지도 못해 고정되었으므로 반복문을 종료한다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;나누어준 번호표와 학생수를 비교하여 모든 학생이 받았는지 확인한 후&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;모든 학생이 받았으면 &quot;Nice&quot;를 그렇지 않으면 &quot;Sad&quot;를 출력한다&lt;/p&gt;
&lt;pre id=&quot;code_1736124856540&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;n = int(input())  # 학생 수
ticket_list = list(map(int, input().split()))  # 학생들 줄 순서

queue = []  # 대기 공간
num = 1     # 현재 와야 할 학생의 번호표

while ticket_list or queue:
    # queue의 맨 앞 학생이 현재 번호와 맞는 경우
    if queue and queue[-1] == num:
        queue.pop()  # queue에서 제거
        num += 1
    # ticket_list의 맨 앞 학생이 현재 번호와 맞는 경우
    elif ticket_list and ticket_list[0] == num:
        ticket_list.pop(0)  # ticket_list에서 제거
        num += 1
    # queue로 이동
    elif ticket_list:
        queue.append(ticket_list.pop(0))
    else:
        break

# 모든 학생이 순서대로 간식을 받았는지 확인
if num - 1 == n:
    print(&quot;Nice&quot;)
else:
    print(&quot;Sad&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘</category>
      <author>시케</author>
      <guid isPermaLink="true">https://dev-9rm.tistory.com/245</guid>
      <comments>https://dev-9rm.tistory.com/245#entry245comment</comments>
      <pubDate>Mon, 6 Jan 2025 10:13:33 +0900</pubDate>
    </item>
    <item>
      <title>[DRF] QuerySet과 Instance(error. AttributeError: 'NoneType' object has no attribute 'exists')</title>
      <link>https://dev-9rm.tistory.com/244</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;2024.12.19.목&lt;/i&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;QuerySet&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;QuerySet은 데이터베이스의 객체 목록을 나타내는 Django의 고유 데이터 구조이다&lt;/span&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;ORM으로 질의를 하게되면 QuerySet을 반환하는 것을 볼 수 있다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;다만 지연로딩(lazy loading) 방식을 사용하기 때문에 데이터를 가져오는 때가 되어야만이 쿼리를 실행한다&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734596743400&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 데이터를 액세스하려고 할 때 (예: list(qs), for obj in qs, qs[0]) 쿼리가 실행
qs = MyModel.objects.filter(name=&quot;Example&quot;)  # 아직 쿼리 실행 X
for obj in qs:  # 여기서 쿼리 실행
    print(obj)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;또한 &lt;span style=&quot;color: #666666; text-align: start;&quot;&gt;QuerySet은 불변 객체처럼 작동한다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #666666; text-align: start;&quot;&gt;&amp;rarr; 기존의 &lt;span style=&quot;color: #666666; text-align: start;&quot;&gt;QuerySet을 변경하지 않고 항상 새로운 &lt;span style=&quot;color: #666666; text-align: start;&quot;&gt;QuerySet 반환&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Model 인스턴스&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;인스턴스라는 표현대로 객체이다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;Django 모델에서 DB 데이터를 Python 객체로 다룰 수 있게 한 것이다&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;QuerySet과 Model 인스턴스&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;그럼 어느때는 QuerySet이고 어느때는 Model 인스턴스일까?&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;답은 개수에 있다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;ORM 쿼리의 결과가 여러 개일 가능성이 있을 경우에는 QuerySet을 반환한다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;다만 반드시 하나일 경우에는 Model 인스턴스를 반환한다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;i&gt;※ 답이 하나라고 반드시 인스턴스인 것은 아니다&lt;/i&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;※&lt;span&gt; &lt;/span&gt;&lt;/span&gt;여러 개일 가능성만 있으면 QuerySet 반환된다&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734597280496&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# QuerySet인 경우
qs = MyModel.objects.filter(name=&quot;Alice&quot;)
qs = MyModel.objects.all()
qs = MyModel.objects.exclude(name=&quot;Alice&quot;)
qs = MyModel.objects.order_by('name')
qs = MyModel.objects.distinct('name')&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1734597323411&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 인스턴스 반환할 경우
obj = MyModel.objects.get(id=1)				# 없으면 DoesNotExist Error 많으면 MultipleObjectsReturned Error
obj = MyModel.objects.filter(name=&quot;Alice&quot;).first()	# 없으면 None
obj = MyModel.objects.filter(name=&quot;Alice&quot;).last()	# 없으면 None&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Exists와 is None 혼용 시 에러 &lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Queryset과 인스턴스 조회 후 존재하는지 확인하는 로직 또한 자주 사용한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이때 Queryset인지 혹은 인스턴스인지에 따라 사용하는 메서드가 다르다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; Queryset &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Queryset에 is None 사용시 항상 &lt;b&gt;False&lt;/b&gt;를 반환한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Queryset이 비었는지 확인하기 위해서는 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;b&gt;exists()&lt;/b&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;를 사용해야 한다&lt;/p&gt;
&lt;pre id=&quot;code_1734598264343&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;qs = MyModel.objects.filter(name=&quot;Nonexistent&quot;)
if qs is None:
    print(&quot;QuerySet is None&quot;)  # 이 조건은 절대 참이 되지 않음

# 특정 조건에 맞는 객체가 존재하는지 확인
if qs.exists():
    print(&quot;객체가 존재합니다.&quot;)
else:
    print(&quot;객체가 존재하지 않습니다.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Model 인스턴스&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인스턴스 객체가 비어있는지 확인할때는 &lt;b&gt;is None&lt;/b&gt;을 사용해야 한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인스턴스 객체에 exists() 메서드를 사용할 경우 &lt;b&gt;AttributeError&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;span style=&quot;color: #333333; text-align: start;&quot;&gt;exists() 메서드는 QuerySet이 가지고 있는 메서드이지 인스턴스 객체는 해당 메서드를 가지고 있지 않기 때문이다&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734598396396&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 특정 조건에 맞는 첫 번째 객체가 None인지 확인
obj = MyModel.objects.filter(name=&quot;Alice&quot;).first()
if obj is None:
    print(&quot;객체가 존재하지 않습니다.&quot;)
else:
    print(f&quot;객체가 존재합니다: {obj.name}&quot;)

if obj.exists():  # AttributeError 발생
    print(&quot;Object exists&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Python/Django REST Framework</category>
      <author>시케</author>
      <guid isPermaLink="true">https://dev-9rm.tistory.com/244</guid>
      <comments>https://dev-9rm.tistory.com/244#entry244comment</comments>
      <pubDate>Thu, 19 Dec 2024 17:55:05 +0900</pubDate>
    </item>
    <item>
      <title>[DRF] From Data로 Boolean 타입 받기(feat. Serializer)</title>
      <link>https://dev-9rm.tistory.com/243</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;2024.12.18.수&lt;/i&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;From Data&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;Front-end에서 Form Data를 활용하여 Boolean 값을 전송하는 경우&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;해당 값은 &quot;문자열&quot;로 전달된다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;이는 적절히 변환하여 전송해야 한다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;JavaScript의 FormData는 &lt;b&gt;문자열&lt;/b&gt; 혹은 &lt;b&gt;BLOB&lt;/b&gt;으로 처리한다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;number나 boolean 등의 값은 처리하지 못하므로&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;Boolean 값을 직접 &lt;b&gt;문자열로 변환해서&lt;/b&gt; 전송해야 한다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734501587356&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// FormData 객체를 사용하는 경우
const formData = new FormData();
formData.append('is_active', 'true'); // Boolean 값을 문자열로 변환&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1734501667093&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Axios로 Form Data 전송
import axios from 'axios';

const formData = new FormData();
formData.append('is_active', 'true'); // 'true' 또는 'false'로 전송

axios.post('/api/endpoint/', formData, {
    headers: {
        'Content-Type': 'multipart/form-data',
    },
});&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1734501697000&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- HTML Form을 사용하는 경우 --&amp;gt;
&amp;lt;form method=&quot;POST&quot; action=&quot;/api/endpoint/&quot;&amp;gt;
    &amp;lt;input type=&quot;hidden&quot; name=&quot;is_active&quot; value=&quot;false&quot;&amp;gt; &amp;lt;!-- 기본값 --&amp;gt;
    &amp;lt;input type=&quot;checkbox&quot; name=&quot;is_active&quot; value=&quot;true&quot;&amp;gt; &amp;lt;!-- 체크 시 true --&amp;gt;
    &amp;lt;button type=&quot;submit&quot;&amp;gt;Submit&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Server에서 데이터 처리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;다행히 Django REST Framework의 BooleanField는 문자열로 전달된 값을 자동으로 변환해준다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;True&lt;/b&gt;로 처리되는 값&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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;&quot;true&quot;&lt;/li&gt;
&lt;li&gt;&quot;t&quot;&lt;/li&gt;
&lt;li&gt;&quot;yes&quot;&lt;/li&gt;
&lt;li&gt;&quot;y&quot;&lt;/li&gt;
&lt;li&gt;&quot;on&quot;&lt;/li&gt;
&lt;li&gt;&quot;1&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; False&lt;/b&gt;로 처리되는 값&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;false&quot;&lt;/li&gt;
&lt;li&gt;&quot;f&quot;&lt;/li&gt;
&lt;li&gt;&quot;no&quot;&lt;/li&gt;
&lt;li&gt;&quot;n&quot;&lt;/li&gt;
&lt;li&gt;&quot;off&quot;&lt;/li&gt;
&lt;li&gt;&quot;0&quot;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&quot;&quot; (빈 문자열)&amp;nbsp;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;위에 언급되지 않은 값이 전달되면 유효성 검사시 오류를 반환한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;DRF에서는 빈 문자열을 False로 처리하며&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;BooleanField는 기본적으로 None 값을 허용하지 않는다(&quot;allow_null=True&quot; 설정 필요)&lt;/p&gt;
&lt;pre id=&quot;code_1734504690521&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 빈 문자열을 허용
is_active = serializers.BooleanField(allow_blank=False)&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1734504662565&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# None 값 허용
is_active = serializers.BooleanField(allow_null=True)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;또한 DRF는 대소문자를 구분하지 않는다&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;따라서 &quot;TRUE&quot;, &quot;False&quot; 등도 올바르게 처리된다&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;i&gt;예시&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734504612307&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from rest_framework import serializers

class MySerializer(serializers.Serializer):
    is_active = serializers.BooleanField()

# 테스트 데이터
test_data = [
    &quot;true&quot;, &quot;false&quot;, &quot;1&quot;, &quot;0&quot;, &quot;yes&quot;, &quot;no&quot;, &quot;on&quot;, &quot;off&quot;, &quot;&quot;,
    &quot;True&quot;, &quot;False&quot;, &quot;T&quot;, &quot;F&quot;, &quot;Y&quot;, &quot;N&quot;, &quot;Yes&quot;, &quot;No&quot;, &quot;On&quot;, &quot;Off&quot;,
    &quot;abc&quot;, &quot;123&quot;, None
]

for value in test_data:
    serializer = MySerializer(data={&quot;is_active&quot;: value})
    if serializer.is_valid():
        print(f&quot;Input: {value!r} &amp;rarr; Parsed: {serializer.validated_data['is_active']}&quot;)
    else:
        print(f&quot;Input: {value!r} &amp;rarr; Error: {serializer.errors}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;출력결과&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734504631370&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Input: 'true' &amp;rarr; Parsed: True
Input: 'false' &amp;rarr; Parsed: False
Input: '1' &amp;rarr; Parsed: True
Input: '0' &amp;rarr; Parsed: False
Input: 'yes' &amp;rarr; Parsed: True
Input: 'no' &amp;rarr; Parsed: False
Input: 'on' &amp;rarr; Parsed: True
Input: 'off' &amp;rarr; Parsed: False
Input: '' &amp;rarr; Parsed: False
Input: 'True' &amp;rarr; Parsed: True
Input: 'False' &amp;rarr; Parsed: False
Input: 'T' &amp;rarr; Parsed: True
Input: 'F' &amp;rarr; Parsed: False
Input: 'Y' &amp;rarr; Parsed: True
Input: 'N' &amp;rarr; Parsed: False
Input: 'Yes' &amp;rarr; Parsed: True
Input: 'No' &amp;rarr; Parsed: False
Input: 'On' &amp;rarr; Parsed: True
Input: 'Off' &amp;rarr; Parsed: False
Input: 'abc' &amp;rarr; Error: {'is_active': [ErrorDetail(string='Not a valid boolean.', code='invalid')]}
Input: '123' &amp;rarr; Error: {'is_active': [ErrorDetail(string='Not a valid boolean.', code='invalid')]}
Input: None &amp;rarr; Error: {'is_active': [ErrorDetail(string='This field may not be null.', code='null')]}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Python/Django REST Framework</category>
      <author>시케</author>
      <guid isPermaLink="true">https://dev-9rm.tistory.com/243</guid>
      <comments>https://dev-9rm.tistory.com/243#entry243comment</comments>
      <pubDate>Wed, 18 Dec 2024 15:10:05 +0900</pubDate>
    </item>
    <item>
      <title>[DRF] QueryDict과 Class Dict(feat. HTTP 메서드)</title>
      <link>https://dev-9rm.tistory.com/242</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;2024.12.17.화&lt;/i&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Dictionary?&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;이름을 얼핏 들으면 &quot;둘 다 같은 딕셔너리 아닌가?&quot; 라고 생각할 수 있다&lt;/span&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;다만 둘의 사용 맥락과 특징은 다르니 &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;특히 REST API를 사용하는 DRF에서는 차이점을 알아두는 것이 좋다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Class Dict &lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;일반적인 Python의 dict 객체이다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;key-value 쌍으로 데이터를 저장한다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;i&gt; ※&lt;span&gt;&amp;nbsp;&lt;/span&gt; 값에 리스트를 명시적으로 지정하지 않으면 단일 값만 저장한다(여러 값을 저장하려면 수동으로 리스트를 생성 )&lt;/i&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;사용 맥락&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&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;Django 모델에서 데이터를 다룰 때, 모델 인스턴스의 __dict__ 속성을 통해 필드 값에 접근 가능&lt;/li&gt;
&lt;li&gt;Python 전반에서 데이터 저장, 처리 등에 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;__dict__란?&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&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;Python 객체의 __dict__는 객체의 속성을 저장하는 사전(dict)이다&lt;/li&gt;
&lt;li&gt;객체가 가진 모든 속성과 그 값들을 key-value 쌍으로 저장함&lt;/li&gt;
&lt;li&gt;일반적으로 사용자 정의 클래스나 Django 모델에서도 이 속성이 존재하며, 객체 내부 데이터를 확인하거나 조작할 때 사용 가능 함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특징&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;변경 가능&lt;/b&gt;: 자유롭게 값을 추가, 삭제, 수정 가능&lt;/li&gt;
&lt;li&gt;&lt;b&gt;직접 접근 가능&lt;/b&gt;: 키를 통해 데이터를 바로 접근하거나 수정 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;예제&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734500183055&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 일반 dict 예제
data = {&quot;key1&quot;: &quot;value1&quot;, &quot;key2&quot;: &quot;value2&quot;}
print(data[&quot;key1&quot;])		# 출력: value1
data[&quot;key3&quot;] = &quot;value3&quot;		# 값 추가&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Query Dict &lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;Django에서 사용되는 특별한 dict 형태이다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;HTTP 요청(request)의 데이터를 다룰 때 주로 사용한다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;QueryDict는 내부적으로 MultiValueDict 구조를 사용하여 &lt;b&gt;키에 여러 값을 리스트 형태로 저장&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;출력 시 QueryDict 객체라는 정보와 함께 각 키에 대해 &lt;b&gt;항상 리스트 형태로 값이 저장&lt;/b&gt;되어 있는 것을 알 수 있다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;i&gt; ※ Django의 QueryDict 클래스는 django.http.QueryDict에 정의되어 있음&lt;/i&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; 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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;request.GET 또는 request.POST와 같은 &lt;b&gt;요청 데이터&lt;/b&gt;를 처리할 때 사용&lt;/li&gt;
&lt;li&gt;주로 웹 &lt;b&gt;폼 데이터&lt;/b&gt;나 &lt;b&gt;URL 쿼리 매개변수&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Immutable (읽기 전용)&lt;/b&gt;: 기본적으로 &lt;b&gt;변경 불가&lt;/b&gt;하며 데이터를 수정하려면 &lt;b&gt;mutable 속성&lt;/b&gt;을 &lt;b&gt;True&lt;/b&gt;로 설정해야 함&lt;/li&gt;
&lt;li&gt;&lt;b&gt;다중 값 지원&lt;/b&gt;: 하나의 키에 대해 &lt;b&gt;여러 값을 저장&lt;/b&gt; 가능 이를 위해 &lt;b&gt;getlist() 메서드&lt;/b&gt;를 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;예제&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734500478115&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from django.http import QueryDict

# QueryDict 예제
qd = QueryDict(&quot;key1=value1&amp;amp;key2=value2&amp;amp;key1=value3&quot;)
print(qd[&quot;key1&quot;])       # 출력: value1 (첫 번째 값 반환)
print(qd.getlist(&quot;key1&quot;))  # 출력: ['value1', 'value3'] (모든 값 반환)

# 데이터를 수정하려면 mutable 속성 사용
qd = qd.copy()  # 기존 QueryDict는 immutable이므로 복사본 생성
qd[&quot;key3&quot;] = &quot;value4&quot;
print(qd)  # 출력: QueryDict('key1=value1&amp;amp;key1=value3&amp;amp;key2=value2&amp;amp;key3=value4')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;사용 예제&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1734501026216&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# QueryDict의 단일 값 접근
print(query_dict.get('key1'))  # 'value1' (첫 번째 값 반환)
print(query_dict.get('key2'))  # 'value2' (첫 번째 값 반환)

# Python Dictionary의 값 접근
print(python_dict['key1'])  # 'value1'
print(python_dict['key2'])  # ['value2', 'value3'] (리스트 전체 반환)&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1734501040905&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;QueryDict 출력:
&amp;lt;QueryDict: {'key1': ['value1'], 'key2': ['value2', 'value3']}&amp;gt;
QueryDict 내부 구조 (getlist 사용):
['value2', 'value3']

Python Dictionary 출력:
{'key1': 'value1', 'key2': ['value2', 'value3']}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;필요한 경우 QueryDict &amp;harr; Class Dict을 양방향으로 변환 가능하다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;QueryDict &amp;rarr; Class Dict 변환&lt;/p&gt;
&lt;pre id=&quot;code_1734500673894&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;data = dict(request.GET) # 딕셔너리로 변환

# 딕셔너리로 복사하는 방식으로도 활용 가능
data = request.data.copy()&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;Class Dict &amp;rarr; QueryDict 변환&lt;/p&gt;
&lt;pre id=&quot;code_1734500724216&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from django.http import QueryDict

qd = QueryDict('', mutable=True)
qd.update({&quot;key1&quot;: &quot;value1&quot;, &quot;key2&quot;: &quot;value2&quot;})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Python/Django REST Framework</category>
      <author>시케</author>
      <guid isPermaLink="true">https://dev-9rm.tistory.com/242</guid>
      <comments>https://dev-9rm.tistory.com/242#entry242comment</comments>
      <pubDate>Wed, 18 Dec 2024 14:51:17 +0900</pubDate>
    </item>
    <item>
      <title>[DRF] View / Model / Serializer 각각 어디까지 처리하는 것이 이상적인가?</title>
      <link>https://dev-9rm.tistory.com/241</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;2024.12.16.월&lt;/i&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Django 프레임워크에서의 역할 분담&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;Django 프레임워크를 사용하면 여느 프레임워크처럼 각각 역할이 있다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;로직을 처리하는 곳은 목적성과 유지보수성과 같은 것에 따라 사용자가 어느정도 분리하는 것도 있지만&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;프레임워크는 기본적으로 구조를 강제한다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;Django의 구조와 역할 분담이 어떻게 되어있는지 알고 로직을 작성하는 것은 중요하다&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;View&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;view는 기본적으로 비즈니스 로직을 수행하는 곳이다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;즉, 사용자(client)의 요청과 응답을 처리한다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;요청을 받고 필요하다면 여타 Model등을 호출하여 사용하며 알맞는 응답을 반환한다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;Serializer와 Model을 사용하는 주체는 View이다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&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;HTTP 요청 처리 (GET, POST, PUT, DELETE)&lt;/li&gt;
&lt;li&gt;Serializer를 통해 데이터 직렬화/역직렬화&lt;/li&gt;
&lt;li&gt;비즈니스 로직이 필요한 경우 Model 메서드를 호출&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Model&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;Model은 데이터베이스와 관련된 모든 것을 담당한다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;Django는 강력한 ORM 기능을 가지고 있다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;데이터를 정의하고 그에 따른 CRUD를 처리한다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&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;데이터 구조 정의 (fields)&lt;/li&gt;
&lt;li&gt;데이터 저장, 수정, 삭제 (CRUD)&lt;/li&gt;
&lt;li&gt;데이터 관련 &lt;b&gt;비즈니스 로직&lt;/b&gt; (메서드 추가)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Serializer &lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Model의 데이터를 JSON 등으로 변환&lt;/b&gt;하거나, &lt;b&gt;입력된 데이터를 검증&lt;/b&gt;하는 역할을 한다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;Model에 정의되어 있는 내용을 바탕으로 유효한 데이터인지 아닌지 검증하며&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;View에서 사용자가 받기 좋은 형태로 바꿔주는 직렬화를 수행한다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;(사용자가 보낸 데이터를 ORM에 맞는 객체로 바꾸는 것이 역직렬화이다)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&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;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;로직의 의도와 성격&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&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;비즈니스 로직 자체는 View가 수행하는데 왜 Model에서도 수행 가능하다고 하는가?&lt;/li&gt;
&lt;li&gt;유효성 검사는 Model 정의 자체로 가능한것 아닌가? 왜 Serializer에서 수행하는가?&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;View와 Model&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;비즈니스 로직 자체는 View가 수행하는데 왜 Model에서도 수행 가능하다고 하는가?&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;비즈니스 로직 자체는 View가 중심인 것이 맞다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;하지만, 무조건 Model 특정필드가 저장될때 값을 변환하거나 자동으로 날짜 등을 입력하는 경우가 있다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;이 경우 데이터와 관련된 비즈니스 로직이다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;즉, 사용자의 요청을 중심으로 한 비즈니스 로직의 성질 보다는&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;데이터가 알맞게 변환되어야 하기 위해 추가되는 로직에 가깝다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;해당 경우에는 Model에서 별도의 메서드 혹은 save 메서드에 추가하여 실행하도록 한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;View는&amp;nbsp;사용자의&amp;nbsp;요청(Request)을&amp;nbsp;중심으로&amp;nbsp;한&amp;nbsp;비즈니스&amp;nbsp;로직을&amp;nbsp;처리 &lt;br /&gt;&amp;rarr;&amp;nbsp;데이터&amp;nbsp;생성&amp;nbsp;전&amp;nbsp;추가&amp;nbsp;검증,&amp;nbsp;외부&amp;nbsp;API&amp;nbsp;호출,&amp;nbsp;복합적인&amp;nbsp;처리&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Model은&amp;nbsp;데이터의&amp;nbsp;무결성과&amp;nbsp;일관성을&amp;nbsp;유지하기&amp;nbsp;위해&amp;nbsp;필요한&amp;nbsp;로직을&amp;nbsp;처리 &lt;br /&gt;&amp;rarr;&amp;nbsp;필드&amp;nbsp;값&amp;nbsp;변환,&amp;nbsp;자동&amp;nbsp;시간&amp;nbsp;설정,&amp;nbsp;데이터&amp;nbsp;유효성&amp;nbsp;검사&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&quot;데이터 무결성과 관련된 로직&quot;은 모델에 두고,&lt;br /&gt;&quot;사용자의 요청에 따른 흐름 제어&quot;는 뷰에 두는 것이 적절하다&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;models.py&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734337502880&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from django.db import models
from django.utils import timezone

class Product(models.Model):
    name = models.CharField(max_length=100)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    discount = models.FloatField(default=0.0)
    created_at = models.DateTimeField(auto_now_add=True)  # 생성 시 자동으로 현재 시간 저장
    updated_at = models.DateTimeField()  # 수동으로 업데이트

    def save(self, *args, **kwargs):
        # 비즈니스 로직: 가격이 음수면 예외 발생
        if self.price &amp;lt; 0:
            raise ValueError(&quot;Price cannot be negative.&quot;)
        
        # 비즈니스 로직: 할인된 가격 계산
        self.price = self.price * (1 - self.discount)

        # updated_at을 현재 시간으로 업데이트
        self.updated_at = timezone.now()

        super().save(*args, **kwargs)

    def get_discounted_price(self):
        return self.price * (1 - self.discount)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;views.py&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734337532947&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 예제 데이터 생성
product = Product(name=&quot;Sample Product&quot;, price=1000, discount=0.1)
product.save()

print(product.created_at)  # 생성된 시간 출력
print(product.updated_at)  # 저장될 때의 현재 시간 출력

# 데이터 수정
product.price = 1200
product.save()

print(product.updated_at)  # 수정된 시간으로 갱신됨&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Model과 Serializer&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;유효성 검사는 Model 정의 자체로 가능한것 아닌가? 왜 Serializer에서 수행하는가?&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;먼저 Model에 정의되어 있기 때문에 검증하지 않고 Model에 넘겨버리는 것은&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;기본 역할 자체를 떠넘기는 행위이다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;이는 View에서도 Model에서 알아서 저장되지 않는 무결하지 않은 데이터를 넘기는 것도 해당한다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;Front-end는 유효하지 않은 데이터를 Back-end로 전달하면 안되며&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;Back-end는 무결하지 않은 혹은 CRUD를 수행하지 않을 데이터를 Model로 전달하면 안된다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;Model은 기본적인 로직 처리는 가능하지만 &lt;/span&gt;&lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;&quot;유효성 검사&quot;를 처리하도록 하면 안된다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;Serializer의 목적성은 client와 server 간의 데이터 송수신시 데이터를 알맞게 변환(직렬화/역직렬화)하고&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;Model에 보내도 되는 데이터인지 검증한다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;또한 비즈니스 로직마다 수행해야 하는 유효성 검사가 다르거나 특정필드의 포함 유무가 다를 수 있기도 하다&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;Model&lt;/b&gt;: 데이터베이스와 관련된 기본적인 유효성 검사를 처리&lt;br /&gt;&lt;b&gt;Serializer&lt;/b&gt;: 클라이언트 요청 데이터를 변환하고 비즈니스 로직에 맞는 유효성 검사를 수행&lt;br /&gt;&lt;b&gt;View&lt;/b&gt;: Serializer를 사용해 데이터를 검증하고, 유효한 데이터만 Model에 저장&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt; Model&lt;/b&gt;은 데이터베이스 저장과 관련된 기본적인 유효성 검사(예: 필드 타입, 필수 값)를 처리하지만, &lt;br /&gt;비즈니스 로직이나 요청 상황에 따른 유효성 검사는 수행하지 않는다&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt; Serializer&lt;/b&gt;가 요청 데이터를 검증하고, Model에 유효한 데이터만 넘긴다&lt;br /&gt;특정 비즈니스 로직에 따라 유효성 검사 조건이 달라질 수 있으므로,&lt;br /&gt;View와 API 레이어에서 검증을 추가하는 것이 효율적이다&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;models.py&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734338837910&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from django.db import models

class UserProfile(models.Model):
    name = models.CharField(max_length=50)  # 필수 입력
    age = models.IntegerField()  # 나이: 양수여야 함
    created_at = models.DateTimeField(auto_now_add=True)  # 생성 날짜 자동 저장

    def __str__(self):
        return self.name&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;serializers.py&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734338886756&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from rest_framework import serializers
from .models import UserProfile

class UserProfileSerializer(serializers.ModelSerializer):
    class Meta:
        model = UserProfile
        fields = ['name', 'age', 'created_at']

    # 추가 유효성 검사: 나이는 0보다 커야 함
    def validate_age(self, value):
        if value &amp;lt;= 0:
            raise serializers.ValidationError(&quot;나이는 0보다 커야 합니다.&quot;)
        return value&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #666666; text-align: left;&quot;&gt;views.py&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1734338909454&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import UserProfile
from .serializers import UserProfileSerializer

class UserProfileView(APIView):
    def post(self, request):
        serializer = UserProfileSerializer(data=request.data)
        
        # 유효성 검사 수행
        if serializer.is_valid():
            serializer.save()  # 검증 통과 후 Model에 저장
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Python/Django REST Framework</category>
      <author>시케</author>
      <guid isPermaLink="true">https://dev-9rm.tistory.com/241</guid>
      <comments>https://dev-9rm.tistory.com/241#entry241comment</comments>
      <pubDate>Mon, 16 Dec 2024 17:54:08 +0900</pubDate>
    </item>
    <item>
      <title>[VScode 오류]파이썬 자동 줄정렬 안됨(There is no formatter for 'python' files installed.)</title>
      <link>https://dev-9rm.tistory.com/240</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;2023.12.07.목&lt;/i&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 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;There&amp;nbsp;is&amp;nbsp;no&amp;nbsp;formatter&amp;nbsp;for&amp;nbsp;'python'&amp;nbsp;files&amp;nbsp;installed.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&quot;autopep8&quot;&lt;/span&gt;&amp;nbsp;혹은&amp;nbsp;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&quot;Black Formatter&quot; 등의 파이썬 formater 확장이 설치되어 있음에도 불구하고&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;줄정렬 시도시(Ctrl + Shitt + F) formatter가 없다고 나오는 오류이다&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;해결&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;파이썬 확장 버전을 변경한다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;v2023.20.0&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;span&gt; 에서 오류가 나서&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;v2023.18.0 버전으로 변경하였다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;현재 v2023.20.0&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;span&gt; 버전에 오류가 있는 듯하다&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f2328; text-align: start;&quot;&gt;&lt;span&gt;이외에 다른 확장이나 설치 등 변경 한것이 없다&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;882&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DW1b2/btsBBNQXBds/kwUWAzslOnWGE70vicbBh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DW1b2/btsBBNQXBds/kwUWAzslOnWGE70vicbBh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DW1b2/btsBBNQXBds/kwUWAzslOnWGE70vicbBh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDW1b2%2FbtsBBNQXBds%2FkwUWAzslOnWGE70vicbBh1%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;1404&quot; height=&quot;882&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;882&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; 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;935&quot; data-origin-height=&quot;207&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l6j93/btsByQOeRan/jdB9UrxZq9O0yEQlOk2Js1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l6j93/btsByQOeRan/jdB9UrxZq9O0yEQlOk2Js1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l6j93/btsByQOeRan/jdB9UrxZq9O0yEQlOk2Js1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl6j93%2FbtsByQOeRan%2FjdB9UrxZq9O0yEQlOk2Js1%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;935&quot; height=&quot;207&quot; data-origin-width=&quot;935&quot; data-origin-height=&quot;207&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;해당 이슈 참고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/microsoft/vscode-python/issues/22412&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/microsoft/vscode-python/issues/22412&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1701934622381&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;python There is no formatter for 'python' files installed.  &amp;middot; Issue #22412 &amp;middot; microsoft/vscode-python&quot; data-og-description=&quot;The latest vscode causes python's formatting buttons to disappear, even though I have all the relevant python plugins and autopep8 installed!&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/microsoft/vscode-python/issues/22412&quot; data-og-url=&quot;https://github.com/microsoft/vscode-python/issues/22412&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/8bMax/hyUID9JrLJ/tQk5xH32klMdgnW0P8X36k/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/microsoft/vscode-python/issues/22412&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/microsoft/vscode-python/issues/22412&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/8bMax/hyUID9JrLJ/tQk5xH32klMdgnW0P8X36k/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;python There is no formatter for 'python' files installed. &amp;middot; Issue #22412 &amp;middot; microsoft/vscode-python&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The latest vscode causes python's formatting buttons to disappear, even though I have all the relevant python plugins and autopep8 installed!&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>오류</category>
      <author>시케</author>
      <guid isPermaLink="true">https://dev-9rm.tistory.com/240</guid>
      <comments>https://dev-9rm.tistory.com/240#entry240comment</comments>
      <pubDate>Thu, 7 Dec 2023 16:37:17 +0900</pubDate>
    </item>
    <item>
      <title>[JavaScript] 함수</title>
      <link>https://dev-9rm.tistory.com/239</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;2023.12.04.월&lt;/i&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;함수&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;함수는 재사용 가능한 코드 블록을 정의하는 데 사용된다&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;함수를 사용하면 특정 작업을 수행하는 코드를 그룹화하고 호출할 수 있다&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;함수 정의와 호출&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;`function` 키워드를 사용하여 정의하며&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;함수의 이름, 매개변수 목록, 중괄호로 둘러싸인 코드블록으로 구성된다&lt;/p&gt;
&lt;pre id=&quot;code_1701667062541&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 함수 정의
function add(a, b) {
  return a + b;
}

// 함수 호출
let sum = add(3, 5);
console.log(sum); // 출력 결과: 8&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수의 매개변수에 기본값을 설정할 수 있다(ES6부터 가능)&lt;/p&gt;
&lt;pre id=&quot;code_1701667205770&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function greet(name = &quot;Guest&quot;) {
  console.log(`Hello, ${name}!`);
}

greet();      // 출력 결과: Hello, Guest!
greet(&quot;John&quot;); // 출력 결과: Hello, John!&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;기명함수와 익명함수&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;기명함수는 앞서 다루었던 흔한 형태의 함수이다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;말그대로 이름이 있는 함수이다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;이름으로 호출하여 여러 번 사용가능하고 참조할 수 있다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;주로 함수를 여러 곳에 재사용하거나 재귀적인 호출을 할 때 쓰인다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;반면에 익명함수는 이름이 없는 함수이다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;변수에 할당되거나 다른 함수의 매개변수로 사용된다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;주로 한번만 사용되거나 특정 컨텍스트에서만 필요한 작은 코드 블록을 정의할 때 사용된다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;클로저(Clousure) , 자유 변수 캡처를 생성할 때 사용된다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;클로저는 함수내에서 외부 변수를 참조하는 함수를 의미하며 비동기나 콜백 함수에서 활용된다&lt;/p&gt;
&lt;pre id=&quot;code_1701667594076&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 기명 함수의 예제
function add(a, b) {
  return a + b;
}

// 함수 호출
let result = add(3, 5);
console.log(result); // 출력 결과: 8&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1701667599819&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 익명 함수의 예제
let multiply = function(x, y) {
  return x * y;
};

// 변수에 할당된 익명 함수 호출
let result = multiply(4, 6);
console.log(result); // 출력 결과: 24&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;한번만 사용할 것인데도 그냥 코드블럭으로 두는 것이 아닌 익명 함수를 정의하는 것에 의문이 들 수 있다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;이는 &lt;b&gt;가독성과 목적성&lt;/b&gt;을 위해 코드 블럭을 묶거나 &lt;b&gt;변수의 스코프&lt;/b&gt; 관리를 하거나&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;콜백 함수&lt;/b&gt;로 활용 등의 여러 장점이 있을 수 있다&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;화살표 함수&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;ES6 부터 도입된 함수의 새로운 표현 방식이다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;보다 더 간결하게 함수를 정의할 수 있고&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;함수 내부에 `this`를 더 직관적으로 다룰 수 있다&lt;/p&gt;
&lt;pre id=&quot;code_1701671701440&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 일반 함수 표현식
const add = function(a, b) {
  return a + b;
};

// 화살표 함수
const addArrow = (a, b) =&amp;gt; a + b;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;화설표 함수는 화살표`=&amp;gt;`를 통해 표현 한다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;중괄호 생략시 표현식의 결과가 바로 반환된다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;매개변수가 하나일 경우에는 소괄호 마저도 생략 가능하다&lt;/p&gt;
&lt;pre id=&quot;code_1701671854451&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 일반 함수 표현식
const square = function(x) {
  return x * x;
};

// 화살표 함수 (단축된 형태)
const squareArrow = x =&amp;gt; x * x;&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;화살표 함수는 자신의 `this`를 생성하지 않고 주변의 컨텍스트에서 `this`를 가져온다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;일반 함수가 동작하는 방식과 차이가 있다&lt;/p&gt;
&lt;pre id=&quot;code_1701671952572&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Counter() {
  this.count = 0;

  // 일반 함수에서의 this
  setInterval(function() {
    this.count++; // 이 경우 this는 전역 객체(window)를 가리킴
    console.log(this.count);
  }, 1000);
}

const counter = new Counter();&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1701671957141&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Counter() {
  this.count = 0;

  // 화살표 함수에서의 this
  setInterval(() =&amp;gt; {
    this.count++; // 이 경우 this는 Counter 객체를 가리킴
    console.log(this.count);
  }, 1000);
}

const counter = new Counter();&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;콜백함수&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;다른&amp;nbsp;함수의&amp;nbsp;인수로&amp;nbsp;전달되어&amp;nbsp;특정&amp;nbsp;이벤트나&amp;nbsp;조건&amp;nbsp;발생시&amp;nbsp;실행되는&amp;nbsp;함수이다&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;비동기 작업 시 주로 사용된다&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1701672312273&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 콜백 함수 정의
function myCallback() {
  console.log(&quot;콜백 함수가 호출되었습니다.&quot;);
}
// 콜백 함수 인수로 전달
function performAction(callback) {
  console.log(&quot;어떤 동작을 수행합니다.&quot;);
  callback(); // 콜백 함수 호출
}

// performAction 함수 호출 시 myCallback 함수가 콜백으로 전달됨
performAction(myCallback);&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;아래는 비동기작업의 콜백함수 예시이다&lt;/p&gt;
&lt;pre id=&quot;code_1701672541094&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function fetchData(callback) {
  // 비동기 작업 시뮬레이션 (예: AJAX 요청, 파일 읽기)
  setTimeout(function () {
    const data = &quot;비동기 데이터&quot;;
    callback(data); // 작업 완료 후 콜백 함수 호출
  }, 2000); // 2초 뒤에 작업이 완료됨
}

// fetchData 함수 호출 시 데이터를 다루는 콜백 함수를 전달
fetchData(function (result) {
  console.log(&quot;데이터를 받아왔습니다:&quot;, result);
});&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;콜백함수와 비동기는 함께 다룰 내용이 많으므로 따로 다시한번 알아볼 예정이다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web/JavaScript</category>
      <author>시케</author>
      <guid isPermaLink="true">https://dev-9rm.tistory.com/239</guid>
      <comments>https://dev-9rm.tistory.com/239#entry239comment</comments>
      <pubDate>Mon, 4 Dec 2023 15:50:56 +0900</pubDate>
    </item>
  </channel>
</rss>