<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>lks의 코딩일기</title>
    <link>https://coding-lks.tistory.com/</link>
    <description>급하지 않게 확실히 꾸준하게 새롭게



email : ekdan9172@naver.com
github : https://github.com/Lks9172</description>
    <language>ko</language>
    <pubDate>Wed, 8 Jul 2026 07:08:53 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>coding of lks</managingEditor>
    <image>
      <title>lks의 코딩일기</title>
      <url>https://tistory1.daumcdn.net/tistory/3186106/attach/524331023c4b4dce8973adc01fefc615</url>
      <link>https://coding-lks.tistory.com</link>
    </image>
    <item>
      <title>오픈소스 분석하기(2) - 프로젝트 실행하기</title>
      <link>https://coding-lks.tistory.com/186</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오픈소스 프로젝트 Nestjs-boilerplate를&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분석하기에 앞서 clone받아 작성된 문서, docker관련 파일을 살펴보고 로컬 환경에서 실행해보는 작업을 해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;dockercompose &amp;amp; dockerfile 파악하기&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Auto. Boiler-3.jpg&quot; data-origin-width=&quot;3086&quot; data-origin-height=&quot;2182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kV2hB/btsC4tbUZzY/x0SSgtKvD9wObYVp7ra1J0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kV2hB/btsC4tbUZzY/x0SSgtKvD9wObYVp7ra1J0/img.jpg&quot; data-alt=&quot;docker-compose의 서비스들 도식화&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kV2hB/btsC4tbUZzY/x0SSgtKvD9wObYVp7ra1J0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkV2hB%2FbtsC4tbUZzY%2Fx0SSgtKvD9wObYVp7ra1J0%2Fimg.jpg&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;308&quot; height=&quot;218&quot; data-filename=&quot;Auto. Boiler-3.jpg&quot; data-origin-width=&quot;3086&quot; data-origin-height=&quot;2182&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;docker-compose의 서비스들 도식화&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;docker compose&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker compose는 총 4개의 서비스를 오케스트레이션합니다.&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;services:
  postgres:
    image: postgres:16.1-alpine
    ports:
      - ${DATABASE_PORT}:5432
    volumes:
      - boilerplate-db:/var/lib/postgresql/data
    environment:
      POSTGRES_USER: ${DATABASE_USERNAME}
      POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
      POSTGRES_DB: ${DATABASE_NAME}

  maildev:
    build:
      context: .
      dockerfile: maildev.Dockerfile
    ports:
      - ${MAIL_CLIENT_PORT}:1080
      - ${MAIL_PORT}:1025

  adminer:
    image: adminer
    restart: always
    ports:
      - 8080:8080

  # Uncomment to use redis
  # redis:
  #   image: redis:7-alpine
  #   ports:
  #     - 6379:6379

  api:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - ${APP_PORT}:${APP_PORT}

volumes:
  boilerplate-db:&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 서비스들을 살펴보면&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;PostgreSQL 서비스&lt;/b&gt;:&lt;br /&gt;PostgreSQL 데이터베이스 컨테이너를 실행합니다.**&lt;br /&gt;&lt;code&gt;postgres:16.1-alpine&lt;/code&gt;&lt;b&gt;를 기반이미지로 사용하며, 데이터베이스가 **&lt;code&gt;/var/lib/postgresql/data&lt;/code&gt;&lt;/b&gt;에 저장되고 &lt;b&gt;&lt;code&gt;${DATABASE_PORT}&lt;/code&gt;&lt;/b&gt; 포트로 외부와 통신합니다.(데이터를 영속적으로 저장하기 위해 volumes 명시한 부분)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Maildev 서비스&lt;/b&gt;:&lt;br /&gt;로컬에서 이메일을 개발 및 디버깅하기 위한 Maildev를 실행합니다.&lt;br /&gt;&lt;b&gt;&lt;code&gt;maildev.Dockerfile&lt;/code&gt;&lt;/b&gt;을 사용하여 빌드하고 &lt;b&gt;&lt;code&gt;${MAIL_CLIENT_PORT}&lt;/code&gt;&lt;/b&gt;와 &lt;b&gt;&lt;code&gt;${MAIL_PORT}&lt;/code&gt;&lt;/b&gt; 포트로 통신하며 1080포트로 웹 ui에 접속할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Adminer 서비스&lt;/b&gt;:&lt;br /&gt;데이터베이스를 시각화하고 관리하기 위한 Adminer를 실행합니다.&lt;br /&gt;&lt;b&gt;&lt;code&gt;adminer&lt;/code&gt;&lt;/b&gt; 이미지를 사용하여 8080 포트로 접속 가능합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;API 서비스&lt;/b&gt;:&lt;br /&gt;NestJS API를 실행하기 위한 서비스가 구성되었습니다.&lt;br /&gt;이 서비스는 Dockerfile을 사용하여 빌드되고 &lt;b&gt;&lt;code&gt;${APP_PORT}&lt;/code&gt;&lt;/b&gt; 포트로 외부와 통신합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Dockerfile&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;dockerfile&quot;&gt;&lt;code&gt;# 기반 이미지 선택
FROM node:20.10.0-alpine

RUN apk add --no-cache bash
RUN npm i -g @nestjs/cli typescript ts-node

# app 종속성 설치
COPY package*.json /tmp/app/
RUN cd /tmp/app &amp;amp;&amp;amp; npm install

# 실행에 필요한 스크립트 복사 및 준비
COPY . /usr/src/app
RUN cp -a /tmp/app/node_modules /usr/src/app
COPY ./wait-for-it.sh /opt/wait-for-it.sh
RUN chmod +x /opt/wait-for-it.sh
COPY ./startup.relational.dev.sh /opt/startup.relational.dev.sh
RUN chmod +x /opt/startup.relational.dev.sh
RUN sed -i 's/\r//g' /opt/wait-for-it.sh
RUN sed -i 's/\r//g' /opt/startup.relational.dev.sh

WORKDIR /usr/src/app
RUN if [ ! -f .env ]; then cp env-example-relational .env; fi
RUN npm run build

CMD [&quot;/opt/startup.relational.dev.sh&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;wait-for-it.sh&lt;/code&gt; : 스크립트로 db에 종속적으로 nest가 실행되도록 합니다.&lt;br /&gt;&lt;code&gt;startup.relational.dev.sh&lt;/code&gt; : wait-for-it.sh를 실행하고 db마이그레션, 시딩을 진행합니다.&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;ps. 해당 프로젝트는 mongoDB도 지원하며 &lt;a href=&quot;http://startup.document.dev.sh/&quot;&gt;startup.document.dev.sh&lt;/a&gt;, docker-compose.document.yaml, document.Dockerfile 따로 mongoDB용 파일이 작성되어있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 dockerfile, docker-compose를 작성하여 프로젝트를 실행하는걸로 파악됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 진짜 실행해보고 결과를 확인해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;nest, postgresql 실행하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 저장소 clone받기&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;git clone https://github.com/brocoders/nestjs-boilerplate my-app 
cd my-app 
cp env-example-relational .env&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. env파일 복사 및 실행위한 환경변수 변경&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;cd my-app
cp env-example-relational .env

DATABASE_HOST=postgres를 DATABASE_HOST=localhost로 변경
MAIL_HOST=maildev를 MAIL_HOST=localhost로 변경&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. postgres adminer maildev 서비스 컨테이너 올리기(api는 로컬에서 실행)&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;docker compose up -d postgres adminer maildev&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 종속성 설치 및 실행&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 postgres 마이그레이션, 기본 데이터 시딩까지 실행해줍니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt; npm install
 npm run migration:run
 npm run seed:run:relational
 npm run start:dev&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 실행중 종속성 문제로 에러가 발생한 모습입니다.&lt;br /&gt;문제가 되는 패키지를 npm install해서 해결해줍니다.&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt; [오후 10:48:43] Starting compilation in watch mode...

 [오후 10:48:45] Found 0 errors. Watching for file changes.

 Error: Cannot find module '@aws-sdk/abort-controller'
 Require stack:
 - /Users/lks/project/my-app2/node_modules/@aws-sdk/lib-storage/dist-cjs/Upload.js
 - /Users/lks/project/my-app2/node_modules/@aws-sdk/lib-storage/dist-cjs/index.js
 - /Users/lks/project/my-app2/node_modules/multer-s3/index.js
 - /Users/lks/project/my-app2/dist/files/files.module.js
 - /Users/lks/project/my-app2/dist/users/users.module.js
 - /Users/lks/project/my-app2/dist/app.module.js
 - /Users/lks/project/my-app2/dist/main.js
     at Function.Module._resolveFilename (node:internal/modules/cjs/loader:1144:15)
     at Function.Module._load (node:internal/modules/cjs/loader:985:27)
     at Module.require (node:internal/modules/cjs/loader:1235:19)
     at require (node:internal/modules/helpers:176:18)
     at Object.&amp;lt;anonymous&amp;gt; (/Users/lks/project/my-app2/node_modules/@aws-sdk/lib-storage/dist-cjs/Upload.js:4:28)
     at Module._compile (node:internal/modules/cjs/loader:1376:14)
     at Object.Module._extensions..js (node:internal/modules/cjs/loader:1435:10)
     at Module.load (node:internal/modules/cjs/loader:1207:32)
     at Function.Module._load (node:internal/modules/cjs/loader:1023:12)
     at Module.require (node:internal/modules/cjs/loader:1235:19)

 npm install @aws-sdk/abort-controller&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;서비스 별로 실행 확인하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. API 서비스&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;http://localhost:3000/&quot;&gt;http://localhost:3000/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-01-02 오후 10.52.19.png&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;316&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yJjb1/btsCTw9PfvX/T5wV7cwMIc7GvXOtPMMHj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yJjb1/btsCTw9PfvX/T5wV7cwMIc7GvXOtPMMHj0/img.png&quot; data-alt=&quot;서버에서 설정한 응답을 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yJjb1/btsCTw9PfvX/T5wV7cwMIc7GvXOtPMMHj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyJjb1%2FbtsCTw9PfvX%2FT5wV7cwMIc7GvXOtPMMHj0%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;417&quot; height=&quot;134&quot; data-filename=&quot;스크린샷 2024-01-02 오후 10.52.19.png&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;316&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;서버에서 설정한 응답을 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. swagger 문서에 접속하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;http://localhost:3000/docs&quot;&gt;http://localhost:3000/docs&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-01-02 오후 10.50.59.png&quot; data-origin-width=&quot;3484&quot; data-origin-height=&quot;1472&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DdYLV/btsCWLyqkU9/YXkhaRjtGrLqnA5xGfSSlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DdYLV/btsCWLyqkU9/YXkhaRjtGrLqnA5xGfSSlk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DdYLV/btsCWLyqkU9/YXkhaRjtGrLqnA5xGfSSlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDdYLV%2FbtsCWLyqkU9%2FYXkhaRjtGrLqnA5xGfSSlk%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;3484&quot; height=&quot;1472&quot; data-filename=&quot;스크린샷 2024-01-02 오후 10.50.59.png&quot; data-origin-width=&quot;3484&quot; data-origin-height=&quot;1472&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작성된 swagger api문서를 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. adminer 서비스&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;http://localhost:8080/&quot;&gt;http://localhost:8080/&lt;/a&gt;&lt;br /&gt;adminer서비스에 접속해 마이그레이션된 모습을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-01-02 오후 11.11.52.png&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;558&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0qf5a/btsC4u9IURW/OgrGxwUW3KVxeGa5MFqqT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0qf5a/btsC4u9IURW/OgrGxwUW3KVxeGa5MFqqT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0qf5a/btsC4u9IURW/OgrGxwUW3KVxeGa5MFqqT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0qf5a%2FbtsC4u9IURW%2FOgrGxwUW3KVxeGa5MFqqT0%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;212&quot; height=&quot;174&quot; data-filename=&quot;스크린샷 2024-01-02 오후 11.11.52.png&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;558&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-01-02 오후 11.15.29.png&quot; data-origin-width=&quot;2092&quot; data-origin-height=&quot;1656&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ltIhG/btsCUysyTeN/OYFgUDVv6R8Z0dTeLtDLS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ltIhG/btsCUysyTeN/OYFgUDVv6R8Z0dTeLtDLS1/img.png&quot; data-alt=&quot;adminer에 접속한 모습(table확인 가능)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ltIhG/btsCUysyTeN/OYFgUDVv6R8Z0dTeLtDLS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FltIhG%2FbtsCUysyTeN%2FOYFgUDVv6R8Z0dTeLtDLS1%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;592&quot; height=&quot;469&quot; data-filename=&quot;스크린샷 2024-01-02 오후 11.15.29.png&quot; data-origin-width=&quot;2092&quot; data-origin-height=&quot;1656&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;adminer에 접속한 모습(table확인 가능)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. &lt;i&gt;maildev 서비스&lt;br /&gt;&lt;/i&gt;&lt;/b&gt;&lt;a href=&quot;http://localhost:1080/&quot;&gt;http://localhost:1080/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방금 실행해서 아무 메일도 없지만 1080port에 접속이 잘되는 모습을 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-01-02 오후 11.14.18.png&quot; data-origin-width=&quot;2090&quot; data-origin-height=&quot;932&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0WmfO/btsCSAEl5Be/2PXPJca8bmWtWteKIcxUE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0WmfO/btsCSAEl5Be/2PXPJca8bmWtWteKIcxUE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0WmfO/btsCSAEl5Be/2PXPJca8bmWtWteKIcxUE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0WmfO%2FbtsCSAEl5Be%2F2PXPJca8bmWtWteKIcxUE1%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;2090&quot; height=&quot;932&quot; data-filename=&quot;스크린샷 2024-01-02 오후 11.14.18.png&quot; data-origin-width=&quot;2090&quot; data-origin-height=&quot;932&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>etc</category>
      <category>adminer</category>
      <category>Docker</category>
      <category>dockercompose</category>
      <category>maildev</category>
      <category>nest</category>
      <category>NestJS</category>
      <category>opensource</category>
      <category>posergres</category>
      <category>swagger</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/186</guid>
      <comments>https://coding-lks.tistory.com/186#entry186comment</comments>
      <pubDate>Wed, 3 Jan 2024 20:56:11 +0900</pubDate>
    </item>
    <item>
      <title>오픈 소스 분석하기(1) - 프로젝트 선정 및 Overview</title>
      <link>https://coding-lks.tistory.com/185</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오픈 소스 프로젝트를 분석을 해보려 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;목적&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분석을 통한 목표는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. nestjs에서의 보편적인 디렉토리 구조는 어떻게 구성하는지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 환경변수 관리 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 코드 설계&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 세가지 사항에 대한 학습입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;프로젝트 선정 조건&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러므로 선정할 프로젝트의 조건은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. nestjs기반의 백엔드 프로젝트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 어느정도 신뢰성을 가지고있는 프로젝트(github의 오픈소스 중 star, fork의 갯수가 많은 프로젝트를 선정)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 최근에도 꾸준히 관리가 되고 있는 프로젝트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 개발 문서 작성이 잘 되어있는 프로젝트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드 프로젝트는 유명한 프로젝트들도 많고 쉽게 찾을 수 있지만&lt;br /&gt;백엔드 프로젝트는 여러가지 이유로 실제 서비스 프로젝트는 찾기 어려웠고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러던중 nestjs-boilerplate라는 이름의 프로젝트를 찾았습니다.&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;a href=&quot;https://www.reddit.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.reddit.com/&lt;/a&gt; 레딧에서 검색하시는게 더 편했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;nestjs-boilerplate&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름에서도 알 수 있듰이 nestjs를 기반으로 개발된 백엔드 보일러플레이트입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트 프로젝트도 함께 개발되어있지만 해당 저장소는 백엔드 소스만 취급하며 일단 백엔드 저장소만 분석할 예정입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;- 보일러플레이트란?&lt;br /&gt;컴퓨터 프로그래밍에서 보일러플레이트 또는 보일러플레이트 코드라고 부르는 것은 최소한의 변경으로 여러곳에서 재사용되며, 반복적으로 비슷한 형태를 띄는 코드를 말한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 아래와 같은 기능을 지원합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Database. TypeORM과 Mongoose 지원&lt;/li&gt;
&lt;li&gt;Seeding.&lt;/li&gt;
&lt;li&gt;Config Service (&lt;a href=&quot;https://www.npmjs.com/package/@nestjs/config&quot;&gt;@nestjs/config&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;메일링 서비스 (&lt;a href=&quot;https://www.npmjs.com/package/nodemailer&quot;&gt;nodemailer&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;이메일 기반 로그인 및 회원가입&lt;/li&gt;
&lt;li&gt;소셜 로그인(Apple, Facebook, Google, Twitter).&lt;/li&gt;
&lt;li&gt;관리자 및 사용자 role분할&lt;/li&gt;
&lt;li&gt;국제화/번역 (I18N) (&lt;a href=&quot;https://www.npmjs.com/package/nestjs-i18n&quot;&gt;nestjs-i18n&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;파일 업로드. 로컬 및 Amazon S3 drivers 지원&lt;/li&gt;
&lt;li&gt;Swagger를 통한 api문서.&lt;/li&gt;
&lt;li&gt;E2E and units tests.&lt;/li&gt;
&lt;li&gt;Docker.&lt;/li&gt;
&lt;li&gt;CI (Github Actions).&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1704122405572&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;GitHub - brocoders/nestjs-boilerplate: NestJS boilerplate. Auth, TypeORM, Mongoose, Postgres, MongoDB, Mailing, I18N, Docker.&quot; data-og-description=&quot;NestJS boilerplate. Auth, TypeORM, Mongoose, Postgres, MongoDB, Mailing, I18N, Docker. - GitHub - brocoders/nestjs-boilerplate: NestJS boilerplate. Auth, TypeORM, Mongoose, Postgres, MongoDB, Maili...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/brocoders/nestjs-boilerplate&quot; data-og-url=&quot;https://github.com/brocoders/nestjs-boilerplate&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/gNCpT/hyUXSFaICb/optZOd6hjC1dFeM5K9kog0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/brocoders/nestjs-boilerplate&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/brocoders/nestjs-boilerplate&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/gNCpT/hyUXSFaICb/optZOd6hjC1dFeM5K9kog0/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;GitHub - brocoders/nestjs-boilerplate: NestJS boilerplate. Auth, TypeORM, Mongoose, Postgres, MongoDB, Mailing, I18N, Docker.&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;NestJS boilerplate. Auth, TypeORM, Mongoose, Postgres, MongoDB, Mailing, I18N, Docker. - GitHub - brocoders/nestjs-boilerplate: NestJS boilerplate. Auth, TypeORM, Mongoose, Postgres, MongoDB, Maili...&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;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;도커 컨테이너 구성&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Auto. Boiler-3.jpg&quot; data-origin-width=&quot;3086&quot; data-origin-height=&quot;2182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buWow8/btsCZgK3mva/ecSLgKJ7zI7bn2on6FgcoK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buWow8/btsCZgK3mva/ecSLgKJ7zI7bn2on6FgcoK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buWow8/btsCZgK3mva/ecSLgKJ7zI7bn2on6FgcoK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuWow8%2FbtsCZgK3mva%2FecSLgKJ7zI7bn2on6FgcoK%2Fimg.jpg&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;460&quot; height=&quot;325&quot; data-filename=&quot;Auto. Boiler-3.jpg&quot; data-origin-width=&quot;3086&quot; data-origin-height=&quot;2182&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 항목을 간단히 설명하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Nestjs : 해당 프로젝트에 사용한 서버 프레임워크&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- PostgreSQL : 해당 프로젝트에서 사용한 RDBMS&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Adminer : 데이터베이스 관리 도구로 Docker Hub의 이미지를 사용해 컨테이너 구축&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- maildev : 로컬환경에서 이메일을 보낼 수 있는 smtp서버 및 인터페이스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;디렉터리 구조 도식화&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 nestjs-boilerplate 프로젝트의 소스트리입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러가지 디렉터리와 파일이 많지만 저는 src, docs, docker관련 파일들 위주로 살펴보게될겁니다.&lt;/p&gt;
&lt;pre id=&quot;code_1704123305336&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;├── CODE_OF_CONDUCT.md
├── Dockerfile
├── LICENSE
├── Procfile
├── README.md
├── docker-compose.ci.yaml
├── docker-compose.yaml
├── docs
│   ├── auth.md
│   ├── database.md
│   ├── file-uploading.md
│   ├── installing-and-running.md
│   ├── introduction.md
│   ├── readme.md
│   └── serialization.md
├── e2e.Dockerfile
├── env-example
├── maildev.Dockerfile
├── nest-cli.json
├── package-lock.json
├── package.json
├── renovate.json
├── src
│   ├── app.module.ts
│   ├── auth
│   ├── auth-apple
│   ├── auth-facebook
│   ├── auth-google
│   ├── auth-twitter
│   ├── config
│   ├── database
│   ├── files
│   ├── home
│   ├── i18n
│   ├── mail
│   ├── mailer
│   ├── main.ts
│   ├── roles
│   ├── session
│   ├── social
│   ├── statuses
│   ├── users
│   └── utils
├── startup.ci.sh
├── startup.dev.sh
├── test
│   ├── admin
│   ├── jest-e2e.json
│   ├── user
│   └── utils
├── tsconfig.build.json
├── tsconfig.json
└── wait-for-it.sh&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오픈 소스 프로젝트를 분석하기 위한 프로젝트 선정했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 서비스하는 프로젝트는 아니지만 star, fork 갯수도 많고 docs가 잘 구성되어있고 디렉터리 구조도 잘 잡혀 있다고 생각하는 프로젝트를 선정해서 만족고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 프로젝트 실행, DB스키마 분석, API분석 등의 단계를 따라 진행할 예정입니다 :)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ps 아래는 오픈 소스를 분석하기 위해 참고했던 포스팅 들입니다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1704126647317&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;소스 코드 분석하는 현명한 방법? C++ 개발자 정리법&quot; data-og-description=&quot;서론은 생략하고 본론입니다. 저는 c++ 소스코드 분석은 아래처럼 합니다. 사내 시스템이 있다면 아래처럼 해도 됩니다. 왜냐면 문서와 주석에 주요 성능 이슈나 기타 사항들이 많으니까요. 하지&quot; data-og-host=&quot;coderlife.tistory.com&quot; data-og-source-url=&quot;https://coderlife.tistory.com/137&quot; data-og-url=&quot;https://coderlife.tistory.com/137&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ckOpzB/hyUXL67jXD/G9kt0TuCheEO5VszbEgyXk/img.jpg?width=720&amp;amp;height=472&amp;amp;face=234_225_297_293,https://scrap.kakaocdn.net/dn/y33RM/hyUXUwd8Xz/cgsh3Wt5gSBkgLvmK6PNw0/img.jpg?width=720&amp;amp;height=472&amp;amp;face=234_225_297_293,https://scrap.kakaocdn.net/dn/lvMdR/hyUXNRprt9/rEqKKUZZIWnWHxpkAf1T20/img.jpg?width=720&amp;amp;height=497&amp;amp;face=0_0_720_497&quot;&gt;&lt;a href=&quot;https://coderlife.tistory.com/137&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://coderlife.tistory.com/137&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ckOpzB/hyUXL67jXD/G9kt0TuCheEO5VszbEgyXk/img.jpg?width=720&amp;amp;height=472&amp;amp;face=234_225_297_293,https://scrap.kakaocdn.net/dn/y33RM/hyUXUwd8Xz/cgsh3Wt5gSBkgLvmK6PNw0/img.jpg?width=720&amp;amp;height=472&amp;amp;face=234_225_297_293,https://scrap.kakaocdn.net/dn/lvMdR/hyUXNRprt9/rEqKKUZZIWnWHxpkAf1T20/img.jpg?width=720&amp;amp;height=497&amp;amp;face=0_0_720_497');&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;소스 코드 분석하는 현명한 방법? C++ 개발자 정리법&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;서론은 생략하고 본론입니다. 저는 c++ 소스코드 분석은 아래처럼 합니다. 사내 시스템이 있다면 아래처럼 해도 됩니다. 왜냐면 문서와 주석에 주요 성능 이슈나 기타 사항들이 많으니까요. 하지&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;coderlife.tistory.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;
&lt;figure id=&quot;og_1704126690489&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;오픈소스: 코드 분석 어떻게 하나? | Popit&quot; data-og-description=&quot;Hadoop이나 HBase 등과 같은 솔루션은 문서도 부족하지만 문서로만 설명할 수 없는 복잡한 내용으로 구성되어 있는 솔루션이 많습니다. 따라서 제대로 솔루션을 사용하거나 운영하기 위해서는 소&quot; data-og-host=&quot;www.popit.kr&quot; data-og-source-url=&quot;https://www.popit.kr/%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4-%EC%BD%94%EB%93%9C-%EB%B6%84%EC%84%9D-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%95%98%EB%82%98/&quot; data-og-url=&quot;http://www.popit.kr/오픈소스-코드-분석-어떻게-하나/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/esd3Iu/hyUXOQkd66/7FLsheHRfo2SPzUK1rNc8k/img.jpg?width=585&amp;amp;height=408&amp;amp;face=0_0_585_408,https://scrap.kakaocdn.net/dn/OJYwf/hyUXKmQNNc/VnXwXh2fOLKeWihrc0b0q0/img.jpg?width=585&amp;amp;height=408&amp;amp;face=0_0_585_408,https://scrap.kakaocdn.net/dn/kQBap/hyUTA7flof/xjHNN5lkLsGqBAezGKzSlk/img.png?width=600&amp;amp;height=434&amp;amp;face=0_0_600_434&quot;&gt;&lt;a href=&quot;https://www.popit.kr/%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4-%EC%BD%94%EB%93%9C-%EB%B6%84%EC%84%9D-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%95%98%EB%82%98/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.popit.kr/%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4-%EC%BD%94%EB%93%9C-%EB%B6%84%EC%84%9D-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%95%98%EB%82%98/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/esd3Iu/hyUXOQkd66/7FLsheHRfo2SPzUK1rNc8k/img.jpg?width=585&amp;amp;height=408&amp;amp;face=0_0_585_408,https://scrap.kakaocdn.net/dn/OJYwf/hyUXKmQNNc/VnXwXh2fOLKeWihrc0b0q0/img.jpg?width=585&amp;amp;height=408&amp;amp;face=0_0_585_408,https://scrap.kakaocdn.net/dn/kQBap/hyUTA7flof/xjHNN5lkLsGqBAezGKzSlk/img.png?width=600&amp;amp;height=434&amp;amp;face=0_0_600_434');&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;오픈소스: 코드 분석 어떻게 하나? | Popit&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Hadoop이나 HBase 등과 같은 솔루션은 문서도 부족하지만 문서로만 설명할 수 없는 복잡한 내용으로 구성되어 있는 솔루션이 많습니다. 따라서 제대로 솔루션을 사용하거나 운영하기 위해서는 소&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.popit.kr&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;
&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;figure id=&quot;og_1704126755150&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;스타트업 신입 개발일지(02 : 소스 코드 분석하기)&quot; data-og-description=&quot;제대로된 코드를 만들기 위해서는 제대로된 코드를 많이 읽어보면 된다.기존에 사용하는 라이브러리나 플러그인의 코드를 읽어라인상깊었던 소프트웨어 코드를 읽어라존경하는 사람이 만든 &quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@qksud14/jrdev-02&quot; data-og-url=&quot;https://velog.io/@qksud14/jrdev-02&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bLISJP/hyUXVomxmZ/H3utGiHMlIBtkUWEu8a5LK/img.png?width=1280&amp;amp;height=703&amp;amp;face=0_0_1280_703,https://scrap.kakaocdn.net/dn/bifCQ4/hyUXPhnkU7/gA0HUZ820WOI1mw5jYjc2k/img.png?width=1280&amp;amp;height=703&amp;amp;face=0_0_1280_703,https://scrap.kakaocdn.net/dn/bRyoIN/hyUXSFa2qe/bbn5tKWGjhJQcBbTqz84kk/img.jpg?width=1250&amp;amp;height=1051&amp;amp;face=0_0_1250_1051&quot;&gt;&lt;a href=&quot;https://velog.io/@qksud14/jrdev-02&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@qksud14/jrdev-02&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bLISJP/hyUXVomxmZ/H3utGiHMlIBtkUWEu8a5LK/img.png?width=1280&amp;amp;height=703&amp;amp;face=0_0_1280_703,https://scrap.kakaocdn.net/dn/bifCQ4/hyUXPhnkU7/gA0HUZ820WOI1mw5jYjc2k/img.png?width=1280&amp;amp;height=703&amp;amp;face=0_0_1280_703,https://scrap.kakaocdn.net/dn/bRyoIN/hyUXSFa2qe/bbn5tKWGjhJQcBbTqz84kk/img.jpg?width=1250&amp;amp;height=1051&amp;amp;face=0_0_1250_1051');&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;스타트업 신입 개발일지(02 : 소스 코드 분석하기)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;제대로된 코드를 만들기 위해서는 제대로된 코드를 많이 읽어보면 된다.기존에 사용하는 라이브러리나 플러그인의 코드를 읽어라인상깊었던 소프트웨어 코드를 읽어라존경하는 사람이 만든&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&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>etc</category>
      <category>backend</category>
      <category>github</category>
      <category>nest</category>
      <category>open source</category>
      <category>rdbms</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/185</guid>
      <comments>https://coding-lks.tistory.com/185#entry185comment</comments>
      <pubDate>Tue, 2 Jan 2024 01:28:41 +0900</pubDate>
    </item>
    <item>
      <title>DB모델링[슈퍼타입 &amp;amp; 서브타입 관계]</title>
      <link>https://coding-lks.tistory.com/184</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 DB모델링 중 슈퍼타입과 서브타입 관계에 대해 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 정의를 알아봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 슈퍼타입 엔티티 : 상호 배타적인 더 작은 그룹으로 분할 시킬 수 있는 엔티티를 의미한다.&lt;br /&gt;- 슈퍼타입 내의 분해된 그룹을 서브타입 엔티티라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 설명하면 슈퍼타입과 서브타입은 각각의 실체에 공통되는 속성들을 모아놓은 엔티티를 슈퍼타입으로, 개별적인 속성들을 따로 분리해서 각각의 서브타입으로 정의할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 사원 엔티티가 있고, 회사 내에 정규직 사원과 임시직 사원이 있을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정규직 사원은 연봉으로, 임시직 사원은 월급이나 수당으로 급여내역이 정의된다고 할 때 하나의 엔티티로 해당 데이터들을 다룬다면&amp;nbsp;&lt;br /&gt;&lt;u&gt;정규직은 월급과 수당에, 임시직은 연봉에 NULL값이 입력&lt;/u&gt;될 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-07-16 오후 5.01.41.png&quot; data-origin-width=&quot;220&quot; data-origin-height=&quot;380&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d5Bpfg/btsnE98O2V6/sRjmYy4fHwKv7bKc3KzOj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d5Bpfg/btsnE98O2V6/sRjmYy4fHwKv7bKc3KzOj1/img.png&quot; data-alt=&quot;사원 엔티티 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d5Bpfg/btsnE98O2V6/sRjmYy4fHwKv7bKc3KzOj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd5Bpfg%2FbtsnE98O2V6%2FsRjmYy4fHwKv7bKc3KzOj1%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;89&quot; height=&quot;154&quot; data-filename=&quot;스크린샷 2023-07-16 오후 5.01.41.png&quot; data-origin-width=&quot;220&quot; data-origin-height=&quot;380&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;사원 엔티티 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 슈퍼타입과 서브타입 관계를 형성한다면&lt;br /&gt;슈퍼타입 엔티티에는 공통되는 속성들을, 서브타입에는 개별적인 속성들을 따로 분리해 정의할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-07-16 오후 5.05.06.png&quot; data-origin-width=&quot;564&quot; data-origin-height=&quot;560&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OBzi2/btsnGPn9Wn6/SxUozaC0PAoqPEiIbBG6j0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OBzi2/btsnGPn9Wn6/SxUozaC0PAoqPEiIbBG6j0/img.png&quot; data-alt=&quot;슈퍼타입과 서브타입 관계&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OBzi2/btsnGPn9Wn6/SxUozaC0PAoqPEiIbBG6j0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOBzi2%2FbtsnGPn9Wn6%2FSxUozaC0PAoqPEiIbBG6j0%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;325&quot; height=&quot;323&quot; data-filename=&quot;스크린샷 2023-07-16 오후 5.05.06.png&quot; data-origin-width=&quot;564&quot; data-origin-height=&quot;560&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;슈퍼타입과 서브타입 관계&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 슈퍼타입과 서브타입 관계의 종류에는 대표적으로 아래 3가지가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 각각의 테이블로 분리(Identity)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-07-16 오후 5.08.43.png&quot; data-origin-width=&quot;606&quot; data-origin-height=&quot;420&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wk4pZ/btsnSXrTSPi/xF563buK4dAd4jC7CjUCL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wk4pZ/btsnSXrTSPi/xF563buK4dAd4jC7CjUCL0/img.png&quot; data-alt=&quot;Identity 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wk4pZ/btsnSXrTSPi/xF563buK4dAd4jC7CjUCL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwk4pZ%2FbtsnSXrTSPi%2FxF563buK4dAd4jC7CjUCL0%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;307&quot; height=&quot;213&quot; data-filename=&quot;스크린샷 2023-07-16 오후 5.08.43.png&quot; data-origin-width=&quot;606&quot; data-origin-height=&quot;420&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Identity 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;위 예시와 같은 식별 관계이며 슈퍼타입과 서브타입 각각을 테이블로 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 슈퍼타입에 속한 공통속성들만 접근하는 빈도가 높은 경우&lt;br /&gt;- 유연성을 확보해야하는 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 하나의 테이블로 합치기(Rollup)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-07-16 오후 5.10.53.png&quot; data-origin-width=&quot;256&quot; data-origin-height=&quot;416&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NF0S0/btsnF7JmGN7/CUUWFSqO7IijucC8KTmbHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NF0S0/btsnF7JmGN7/CUUWFSqO7IijucC8KTmbHK/img.png&quot; data-alt=&quot;Rollup&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NF0S0/btsnF7JmGN7/CUUWFSqO7IijucC8KTmbHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNF0S0%2FbtsnF7JmGN7%2FCUUWFSqO7IijucC8KTmbHK%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;100&quot; height=&quot;163&quot; data-filename=&quot;스크린샷 2023-07-16 오후 5.10.53.png&quot; data-origin-width=&quot;256&quot; data-origin-height=&quot;416&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Rollup&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;슈퍼타입에 서브타입의 모든 컬럼을 통합하여 하나의 테이블로 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 전체 테이블에 같이 접근하는 경우가 대부분일 때&lt;br /&gt;- 서브타입의 속성 개수가 적은 경우(2개 이하)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 여러 개의 테이블로 분리(Rolldown)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-07-16 오후 5.13.20.png&quot; data-origin-width=&quot;532&quot; data-origin-height=&quot;338&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvDeSs/btsnFL0RBBi/7A02teXbG9Kwz7IWFMPWzK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvDeSs/btsnFL0RBBi/7A02teXbG9Kwz7IWFMPWzK/img.png&quot; data-alt=&quot;Rolldown&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvDeSs/btsnFL0RBBi/7A02teXbG9Kwz7IWFMPWzK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvDeSs%2FbtsnFL0RBBi%2F7A02teXbG9Kwz7IWFMPWzK%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;272&quot; height=&quot;173&quot; data-filename=&quot;스크린샷 2023-07-16 오후 5.13.20.png&quot; data-origin-width=&quot;532&quot; data-origin-height=&quot;338&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Rolldown&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;슈퍼타입을 각각의 서브타입에 추가하여 서브타입별로 테이블 생성.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 공통적인 속성들만 별도로 접근이 필요한 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 슈퍼타입의 속성개수가 적은 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 슈퍼타입과 관계를 맺은 엔티티가 거의 없는 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>데이터베이스</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/184</guid>
      <comments>https://coding-lks.tistory.com/184#entry184comment</comments>
      <pubDate>Sun, 16 Jul 2023 17:15:27 +0900</pubDate>
    </item>
    <item>
      <title>https</title>
      <link>https://coding-lks.tistory.com/183</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 https에 대해서 알아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;https를 알아보기전에 http부터 알아보자면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;http란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hypertext transfer protocol으로 하이퍼 텍스트 전송 프로토콜입니다.(평문으로 데이터 전송)&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;h3 data-ke-size=&quot;size23&quot;&gt;https란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hypertext transfer protocol secure로 http의 보안 취약점을 보완하기 위해 ssl(secure socket layer)이 추가된 프로토콜입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ssl은 서버와 브라우저 사이에서 안전하게 암호화된 연결을 만들 수 있게 도와준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP Message Body를 암호화한다.&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;또한 https를 사용하면 다음과 같은 이점을 챙길 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 보안성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- SEO(검색엔진 최적화)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;SSL/TLS&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ssl 이란?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Netscape Communications Corporation에서 웹 서버와 웹 브라우저간의 보안을 위해 만든 프로토콜(공개키/개인키 대칭키 두방식을 기반으로 사용함)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 어떤 방식으로 암호화와 복호화를 진행하는지 궁금해집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그전에 대칭키와 공개키 방식에 대해 간단하게 살펴보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;대칭키&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대칭키 방식은 동일한 키로 암호화와 복호화를 수행하는 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키만 가지고 있다면 암호화 복호화를 할 수 있음&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;암복호화가 쉬움&lt;/li&gt;
&lt;li&gt;키를 탈취당했을 경우 치명적임&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-07-01 오후 12.21.18.png&quot; data-origin-width=&quot;484&quot; data-origin-height=&quot;214&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tKqAH/btslZ2w8lrn/13BnwacdoWFMR9KsByg4N0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tKqAH/btslZ2w8lrn/13BnwacdoWFMR9KsByg4N0/img.png&quot; data-alt=&quot;개념도&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tKqAH/btslZ2w8lrn/13BnwacdoWFMR9KsByg4N0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtKqAH%2FbtslZ2w8lrn%2F13BnwacdoWFMR9KsByg4N0%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;484&quot; height=&quot;214&quot; data-filename=&quot;스크린샷 2023-07-01 오후 12.21.18.png&quot; data-origin-width=&quot;484&quot; data-origin-height=&quot;214&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;개념도&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;공개키(비대칭키)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로 다른 키로 암호화와 복호화를 수행하는 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공개키(암호화) - 개인키(복호화)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;공개키는 누구든지 가져도 상관이 없습니다. - 보안성 향상&lt;/li&gt;
&lt;li&gt;암호화 연산 시간이 더 소요되어 비용이 크다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-07-01 오후 12.21.35.png&quot; data-origin-width=&quot;482&quot; data-origin-height=&quot;200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tufZW/btsl2eiLFmt/BW3CowRa54OwyxXNFJszQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tufZW/btsl2eiLFmt/BW3CowRa54OwyxXNFJszQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tufZW/btsl2eiLFmt/BW3CowRa54OwyxXNFJszQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtufZW%2Fbtsl2eiLFmt%2FBW3CowRa54OwyxXNFJszQ0%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;482&quot; height=&quot;200&quot; data-filename=&quot;스크린샷 2023-07-01 오후 12.21.35.png&quot; data-origin-width=&quot;482&quot; data-origin-height=&quot;200&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ssl 통신 과정&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;ssl은 공개키 방식으로 대칭키를 전달합니다.&lt;/li&gt;
&lt;li&gt;이 대칭키를 활용해서 암호화와 복호화를 하고, 서버와 브라우저간 통신을 진행합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 ssl 통신과정을 10개 이상의 단계로 나누어 설명하는 글을작성하도록 하겠습니다.&lt;/p&gt;</description>
      <category>etc</category>
      <category>http</category>
      <category>https</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/183</guid>
      <comments>https://coding-lks.tistory.com/183#entry183comment</comments>
      <pubDate>Sat, 1 Jul 2023 12:24:42 +0900</pubDate>
    </item>
    <item>
      <title>첫 퇴사를 하며 드는 생각들</title>
      <link>https://coding-lks.tistory.com/182</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;징징이댑.jpeg&quot; data-origin-width=&quot;550&quot; data-origin-height=&quot;459&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2t8FZ/btr8LOSm93y/OAvnAjlX1iyh6fFJi6VxQk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2t8FZ/btr8LOSm93y/OAvnAjlX1iyh6fFJi6VxQk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2t8FZ/btr8LOSm93y/OAvnAjlX1iyh6fFJi6VxQk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2t8FZ%2Fbtr8LOSm93y%2FOAvnAjlX1iyh6fFJi6VxQk%2Fimg.jpg&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;193&quot; height=&quot;161&quot; data-filename=&quot;징징이댑.jpeg&quot; data-origin-width=&quot;550&quot; data-origin-height=&quot;459&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;최근에 1년 8개월 정도의 짧다면 짧은 첫 회사생활을 마치며 생각나는 것들을 두서없이 적어보는 글입니다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;퇴사 트리거&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사 생활을 하며 진행했던 프로젝트들을 돌아보면 다른 회사에서라면 쉽게 접하지 못했을 프로젝트들도 많고 프론트, 인프라까지 작업을 하며 기술적인 영역이 생각보다 넓은 사람이 되어있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 작년 후반기 부터 회사사정도 안좋아지고 이직에 대해 알아보면서 현재 커리어와 앞으로 만들어갈 커리어에 대해 고민이 많아지기 시작했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭐 마이너한 기술스택이라던가, 개발 효율성을 위한 툴이나 도구에 대해 고려하지 않는 점(아무래도 이 부분이 더 크다)이라던가 계속해서 우물안의 개구리가 되어가고 있는건 아닐까?라는 생각이 많이 들었던것 같다. 지금생각해보면 이런 생각을 은연중에 하고 있어서 회사 외부의 활동들(글또 or Dnd)에도 지속적으로 참여를 했던거 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;퇴사&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 저번달에 퇴사 통보를했고 최근에 퇴사를 하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퇴사할 때는 마냥 마음 편해질 줄 알았는데&lt;br /&gt;퇴사를 하고 나서도 이게 최선인지 확신은 안서지만 잠시 어떤식으로 살아가야 할지 많은 고민을 하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;고민&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;취업을 할지 창업이라던가 새로운 도전을 해볼지, 정말 일하고 싶은 회사는 있는지에 대해 생각을 하다가도 난 어떤 사람이 되고 싶었던 건지 좀 근본적인 의문도 하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근래에 최고로 생각이 많아지는 시간이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전만큼은 개발에 흥미를 잃은건 아닐까라는 생각도 들고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어쨋든 이제 시간은 많고 아직 정해지지 않았지만 다음 단계는 있을테니 그 다음 단계에 대해서 글로 정리를 해야겠다는 과제가 하나 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;정신산만한 글이 완성 되었다.&lt;br /&gt;글도 생각도 천천히 정리 해보기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Life</category>
      <category>퇴사</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/182</guid>
      <comments>https://coding-lks.tistory.com/182#entry182comment</comments>
      <pubDate>Sun, 9 Apr 2023 19:51:09 +0900</pubDate>
    </item>
    <item>
      <title>퍼페티어(puppeteer)사용하여 크롤링 하기</title>
      <link>https://coding-lks.tistory.com/181</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 작년 회사업무를 하면서 굉장히 친해졌던 퍼페티어라는 라이브러리를 찍먹 해보는 글을 작성해 보겠습니다.&lt;br /&gt;주 내용은 제 블로그 게시물들의 정보(제목, 작성날짜)의 수집 자동화 코드를 작성하는 내용이 되겠습니다.&lt;/p&gt;
&lt;p id=&quot;0915f18a-0f6f-4c53-9b57-b31a90c1c3a7&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 id=&quot;969cca49-f503-4dd3-8b33-61dda00a307b&quot; class=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;puppeteer란?&lt;/h3&gt;
&lt;p id=&quot;d55bd3a5-5c24-45c0-ae17-ee395b5d9c5b&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;puppeteer는 구글에서 개발한 &lt;b&gt;Node.js용&lt;/b&gt; Chrome/Chrominm 제어 API입니다. 일반적으로 자동화된 웹 테스트, 웹 스크래핑, 웹 크롤링 등 다양한 웹 자동화 작업을 수행할 때 사용합니다.&lt;/p&gt;
&lt;p id=&quot;1c3d9716-f4ef-4e62-9008-e750f64b714a&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;특히, 마우스 클릭, 키보드 입력, 페이지 스크롤 등의 이벤트를 자동화해야하는 작업을 수행할 때 적합합니다.&lt;/p&gt;
&lt;p id=&quot;80a96558-4c5c-4e26-8a60-64179138825e&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p id=&quot;d9bd3bf9-3c50-48c1-8804-0d9d3bc754ec&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;649b0fa4-d729-4291-95b4-dd8458d615a0&quot; class=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;puppeteer 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;npm 명령어를 통해 puppeteer 패키지를 설치해줍니다&lt;/p&gt;
&lt;pre id=&quot;ce655682-095a-4783-b491-ef96ade20243&quot; class=&quot;coffeescript&quot;&gt;&lt;code&gt;npm i puppeteer&lt;/code&gt;&lt;/pre&gt;
&lt;p id=&quot;121497d5-a0de-49db-9b5a-95425938064e&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;puppeteer설치시에 함께 작동할 최선 버전의 Chromium을 자동으로 다운로드하기 때문에 용량이 꽤나 큽니다.&lt;/p&gt;
&lt;p id=&quot;62d4df42-6fe9-443d-a3d6-bdf52b13e1f2&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;이미 설치된 Chrom/Chrominum을 사용하고 싶다면 Puppeteer Core를 설치하셔서 사용하시면 됩니다(용량을 아낄 수 있습니다)!&lt;/p&gt;
&lt;p id=&quot;a9a1721b-a095-4963-b6c5-511ab1b46a12&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p id=&quot;016fbbcb-aeeb-43a5-84c7-cb59056e0835&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;17598e88-ba3a-4c2f-8d4c-36e30a33950f&quot; class=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;puppeteer 사용해 내 블로그 글 정보 수집하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. puppeteer를 사용해 Chromium띄우기&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;cbf09a67-b3b6-4c66-afc3-b20e0f22bcfb&quot; class=&quot;code javascript&quot;&gt;&lt;code&gt;const  puppeteer = require('puppeteer');

class Crawler {
    browser = null;
    page = null;
  
    async setConfig() {
      this.browser = await puppeteer.launch({
        headless: false,
      });
      this.page = await this.browser.newPage();
    }
}

async function crawl(){
    const crawler = new Crawler();
    await crawler.setConfig();
}

crawl();&lt;/code&gt;&lt;/pre&gt;
&lt;p id=&quot;974250ca-0d73-46d9-a5c2-52c641cacd88&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;먼저 Crawler라는 이름의 클래스를 정의합니다.&lt;/p&gt;
&lt;ul id=&quot;e6a85025-3013-4f53-94a3-d3dbd6c6e2ae&quot; class=&quot;bulleted-list&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;setConfig메서드에서는 Chromium의 설정값을 정의하고 새로운 page를 띄웁니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;9b658b3c-5175-4fe1-b131-16c8c95fb955&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;crawl함수에서는 정의한 Crawler 클래스를 사용해 정보 수집 로직을 실행할겁니다.&lt;/p&gt;
&lt;p id=&quot;fbb09afe-f5bb-46a6-8581-88e042cb5506&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;위 코드를 실행시키면 아래 이미지와 같이 Chromium브라우저가 실행돼고 새로운 page를 띄운 모습을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-03-12 오후 8.46.15.png&quot; data-origin-width=&quot;1548&quot; data-origin-height=&quot;1362&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dNYypk/btr3eftXZli/1mcf1Ihge8bGImkvLEVTrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dNYypk/btr3eftXZli/1mcf1Ihge8bGImkvLEVTrK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dNYypk/btr3eftXZli/1mcf1Ihge8bGImkvLEVTrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdNYypk%2Fbtr3eftXZli%2F1mcf1Ihge8bGImkvLEVTrK%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;606&quot; height=&quot;533&quot; data-filename=&quot;스크린샷 2023-03-12 오후 8.46.15.png&quot; data-origin-width=&quot;1548&quot; data-origin-height=&quot;1362&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 특정 url로 page를 이동시키기&lt;/b&gt;&lt;/p&gt;
&lt;p id=&quot;a452b5da-412e-4a7c-b904-605219db30b8&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;제 블로그의 게시물들의 정보를 수집하기 위해 &lt;a href=&quot;https://coding-lks.tistory.com/category&quot;&gt;https://coding-lks.tistory.com/category&lt;/a&gt; 분류 전체 보기 url로 페이지를 이동시켜 보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;a61dd0d5-aab3-4840-93ce-4781b09fdbb2&quot; class=&quot;code cs&quot;&gt;&lt;code&gt;const  puppeteer = require('puppeteer');

class Crawler {
    browser = null;
    page = null;
    targetUrl = 'https://coding-lks.tistory.com/category';
  
    async setConfig() {
      this.browser = await puppeteer.launch({
        headless: false,
      });
      this.page = await this.browser.newPage();
    }
  
    async accessSite() {
      await this.page.goto(this.targetUrl);
    }
}
  

async function crawl(){
    const crawler = new Crawler();
    await crawler.setConfig();
    await crawler.accessSite();
}

crawl();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 1. targetUrl 변수는 이동시킬 url의 값을 가지게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 2. accessSite메서드를 실행시켜 아래 스크린샷과 같이 targetUrl로 페이지를 이동시키는 동작을 수행하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 3. 현재 page의 블로그 정보(제목과 작성날짜) 수집하기&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-03-12 오후 9.12.25.png&quot; data-origin-width=&quot;1694&quot; data-origin-height=&quot;1504&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VXIHB/btr3iIvA3uY/pt2QAXxqWgONpH7uYQvyW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VXIHB/btr3iIvA3uY/pt2QAXxqWgONpH7uYQvyW0/img.png&quot; data-alt=&quot;targetUrl에 접근한 화면&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VXIHB/btr3iIvA3uY/pt2QAXxqWgONpH7uYQvyW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVXIHB%2Fbtr3iIvA3uY%2Fpt2QAXxqWgONpH7uYQvyW0%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;602&quot; height=&quot;534&quot; data-filename=&quot;스크린샷 2023-03-12 오후 9.12.25.png&quot; data-origin-width=&quot;1694&quot; data-origin-height=&quot;1504&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;targetUrl에 접근한 화면&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;3. blogData 멤버변수와 getPartOfPost메서드를 추가 작성해서 블로그 정보를 수집하기&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;5c24e563-ee41-4e21-bba4-6883f0aa87ad&quot; class=&quot;code typescript&quot;&gt;&lt;code&gt;const  puppeteer = require('puppeteer');

class Crawler {
    browser = null;
    page = null;
    targetUrl = 'https://coding-lks.tistory.com/category';
    blogData = null;
  
    async setConfig() {
        this.browser = await puppeteer.launch({
            headless: false,
        });
        this.page = await this.browser.newPage();
        this.blogData = [];
    }
  
    async accessSite() {
        await this.page.goto(this.targetUrl);
    }

    async getPartOfPost() {
        await this.page.waitForSelector('#body &amp;gt; ul &amp;gt; li', {
            timeout: 1000,
        });

        const posts = await this.page.$$('#body &amp;gt; ul &amp;gt; li');
        for (const post of posts) {
            const name = await post.$eval('a', (e) =&amp;gt; e.innerText);
            const date = await post.$eval('.date', (e) =&amp;gt; e.innerText);
            this.blogData.push({
                name,
                date
            });
        }
    }
}

async function crawl(){
    const crawler = new Crawler();
    await crawler.setConfig();
    await crawler.accessSite();
    await crawler.getPartOfPost();
}

crawl();&lt;/code&gt;&lt;/pre&gt;
&lt;ol id=&quot;f6fcd1b9-bce7-4a66-a6e6-c76fdde2125a&quot; class=&quot;numbered-list&quot; style=&quot;list-style-type: decimal;&quot; start=&quot;1&quot; type=&quot;1&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;puppeteer의 waitForSelector메서드와 특정 요소의 Selector를 사용해 특정요소가 화면에 랜딩 될때까지 기다립니다.&lt;br /&gt;timeout옵션을 사용하여 최대 대기 시간도 설정할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;ol id=&quot;0438a8d0-2823-447e-9ea3-9f6aeac986ed&quot; class=&quot;numbered-list&quot; style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; type=&quot;1&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;puppeteer의 $$메서드를 사용해 DOM에서 특정 selector에 해당하는 요소들의 배열을 가져옵니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;ol id=&quot;0cb1beac-9c3f-4579-a1c3-64183fc476d3&quot; class=&quot;numbered-list&quot; style=&quot;list-style-type: decimal;&quot; start=&quot;3&quot; type=&quot;1&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;원하는 요소들의 배열을 순회하면서 innerText를 사용해 글의 제목과 날짜 데이터를 텍스트로 수집합니다.저는 name는 html태그를, date는 class값을 사용해 가져왔습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;ol id=&quot;96886794-b7f8-4d8d-8caf-4f887a007021&quot; class=&quot;numbered-list&quot; style=&quot;list-style-type: decimal;&quot; start=&quot;4&quot; type=&quot;1&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;수집한 데이터들을 this.blogData에 추가합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p id=&quot;712c410f-22fc-49c3-9931-b1b2750de3a3&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;이런 방식으로 1 페이지의 블로그글의 데이터를 수집할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp;4. 이벤트를 사용해 다음 페이지로 이동하기&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;b7b263ab-7d0c-4969-8d06-8c2548d12c9a&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;const  puppeteer = require('puppeteer');

class Crawler {
    browser = null;
    page = null;
    targetUrl = 'https://coding-lks.tistory.com/category';
    blogData = null;
  
    async setConfig() {
        this.browser = await puppeteer.launch({
            headless: false,
        });
        this.page = await this.browser.newPage();
        this.blogData = [];
    }
  
    async accessSite() {
        await this.page.goto(this.targetUrl);
    }

    async getPartOfPost() {
        await this.page.waitForSelector('#body &amp;gt; ul &amp;gt; li', {
            timeout: 1000,
        });

        const posts = await this.page.$$('#body &amp;gt; ul &amp;gt; li');
        for (const post of posts) {
            const name = await post.$eval('a', (e) =&amp;gt; e.innerText);
            const date = await post.$eval('.date', (e) =&amp;gt; e.innerText);
            this.blogData.push({
                name,
                date
            });
        }
    }

    async moveNextPage() {
        await this.page.click('#paging &amp;gt; a.next');
    }
}

async function crawl(){
    const crawler = new Crawler();
    await crawler.setConfig();
    await crawler.accessSite();
    await crawler.getPartOfPost();
    await crawler.moveNextPage();
}

crawl();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 다음 페이지로 이동할 수 있는 클릭 이벤트가 있는 next요소를 클릭할 수 있도록 moveNextPage메서드를 작성했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-03-12 오후 9.39.17.png&quot; data-origin-width=&quot;134&quot; data-origin-height=&quot;84&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ciNmOM/btr3ofsTcOf/Sa0Lt8ghmLGrkTWjFObNxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ciNmOM/btr3ofsTcOf/Sa0Lt8ghmLGrkTWjFObNxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ciNmOM/btr3ofsTcOf/Sa0Lt8ghmLGrkTWjFObNxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FciNmOM%2Fbtr3ofsTcOf%2FSa0Lt8ghmLGrkTWjFObNxK%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;89&quot; height=&quot;56&quot; data-filename=&quot;스크린샷 2023-03-12 오후 9.39.17.png&quot; data-origin-width=&quot;134&quot; data-origin-height=&quot;84&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5. 모든 블로그 글 수집하는 소스코드 완성하기&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;07a5b898-dca0-4561-b1d0-09428cb95bf7&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;const  puppeteer = require('puppeteer');

class Crawler {
    browser = null;
    page = null;
    targetUrl = 'https://coding-lks.tistory.com/category';
    blogData = null;
  
    async setConfig() {
        this.browser = await puppeteer.launch({
            headless: false,
        });
        this.page = await this.browser.newPage();
        this.blogData = [];
    }
  
    async accessSite() {
        await this.page.goto(this.targetUrl);
    }

    async getPartOfPost() {
        await this.page.waitForSelector('#body &amp;gt; ul &amp;gt; li', {
            timeout: 1000,
        });

        const posts = await this.page.$$('#body &amp;gt; ul &amp;gt; li');
        for (const post of posts) {
            const name = await post.$eval('a', (e) =&amp;gt; e.innerText);
            const date = await post.$eval('.date', (e) =&amp;gt; e.innerText);
            this.blogData.push({
                name,
                date
            });
        }
    }

    async moveNextPage() {
        const flag = await this.page.waitForSelector('#paging &amp;gt; a.next.no-more-next', {
            timeout: 300,
        }).then(e =&amp;gt; true)
            .catch(e =&amp;gt; false);

        if (flag === true)
            return false;
              
        await this.page.click('#paging &amp;gt; a.next');
    }

    async checkLastPage() {
        const flag = await this.page.waitForSelector('#paging &amp;gt; a.next.no-more-next', {
            timeout: 300,
        }).then(e =&amp;gt; true)
          .catch(e =&amp;gt; false);

        return flag;
    }

    async closeBrowser() {
        await this.browser.close();
    }
} 

async function crawl(){
    const crawler = new Crawler();
    await crawler.setConfig();
    await crawler.accessSite();

    for (let i = 1; i &amp;lt;= 100; i++) {
				await crawler.getPartOfPost();
        const flag = await crawler.checkLastPage();
        if (flag === true)
            break;
        await crawler.moveNextPage();
    }
    
    await crawler.closeBrowser();
    console.log(crawler.blogData);
}

crawl();&lt;/code&gt;&lt;/pre&gt;
&lt;ul id=&quot;f801f885-59d8-4cbc-bb51-35ce627c9911&quot; class=&quot;bulleted-list&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;블로그 리스트의 마지막 페이지에 도달했는지 체크하는 메서드(checkLastPage) 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;ul id=&quot;cb6d5605-a3bd-44a6-b2bb-54a2dfbd6301&quot; class=&quot;bulleted-list&quot; style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;브라우져 종료 시키는 메서드(closeBrowser) 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;7af6fd2a-ea33-4959-9c89-e1f2fd3f18d0&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;위 두가지 함수를 추가해 소스 코드를 완성했습니다.&lt;/p&gt;
&lt;p id=&quot;965fcee9-3fa2-45a9-994d-89bb2c27ee57&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;0129a8c7-ae5e-4094-ac46-baf6851739f9&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;f7c87951-a029-451b-8032-282e7b1606d8&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;총 로직이 아래와 같이 진행됩니다!&lt;/p&gt;
&lt;ol id=&quot;70b7e59f-aac6-42fd-b3d0-02ff539beaa5&quot; class=&quot;numbered-list&quot; style=&quot;list-style-type: decimal;&quot; start=&quot;1&quot; type=&quot;1&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;puppeteer 세팅 - setConfig 메서드&lt;/li&gt;
&lt;/ol&gt;
&lt;ol id=&quot;5c23d63d-5541-4b24-9f76-7d3eded253de&quot; class=&quot;numbered-list&quot; style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; type=&quot;1&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;특정 페이지로 접근 - accessSite 메서드&lt;/li&gt;
&lt;/ol&gt;
&lt;ol id=&quot;108cdb1e-4b42-497f-b82c-4e5ab9776a7f&quot; class=&quot;numbered-list&quot; style=&quot;list-style-type: decimal;&quot; start=&quot;3&quot; type=&quot;1&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;페이지 접근하며 반복문 수행
&lt;ol id=&quot;55224d78-d16f-4c48-9ac0-d6ec9aea8fdb&quot; class=&quot;numbered-list&quot; style=&quot;list-style-type: decimal;&quot; start=&quot;1&quot; type=&quot;a&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;해당 페이지의 게시물의 정보 수집 - getPartOfPost 메서드&lt;/li&gt;
&lt;/ol&gt;
&lt;ol id=&quot;94bbc433-f2e5-4f63-bf60-023d2e3e4991&quot; class=&quot;numbered-list&quot; style=&quot;list-style-type: decimal;&quot; start=&quot;2&quot; type=&quot;a&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;마지막 페이지인지 체크 - checkLastPage 메서드&lt;/li&gt;
&lt;/ol&gt;
&lt;ol id=&quot;ae53a96c-b3fc-4c38-971e-4a906dd243a5&quot; class=&quot;numbered-list&quot; style=&quot;list-style-type: decimal;&quot; start=&quot;3&quot; type=&quot;a&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;다음 페이지로 이동 - moveNextPage 메서드&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ol id=&quot;e635c348-6290-4de3-a72e-737742321449&quot; class=&quot;numbered-list&quot; style=&quot;list-style-type: decimal;&quot; start=&quot;4&quot; type=&quot;1&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;브라우져 종료&lt;/li&gt;
&lt;/ol&gt;
&lt;p id=&quot;88a79e18-42f8-42d2-aaa9-ff04efe45cd9&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;40e0e804-1af5-45f8-b373-22043bd76f63&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;8b6a853c-5468-41fd-a695-12ab04e8c1c2&quot; class=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;정리&lt;/h3&gt;
&lt;p id=&quot;defb45cf-076c-49d5-92b1-8aa8876c6de1&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;회사에서 크롤링 업무를 진행하게 되면서 처음 접했던 브라우져 자동화 라이브러리인 puppeteer 찍먹해보는 게시물을 작성하게 되었습니다.&lt;/p&gt;
&lt;p id=&quot;da6a6188-f07e-4997-b481-2aa574d8c9ff&quot; class=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;정리를 해야지 해야지 하면서 미루다가 4,5개월이 지난 지금에야 처음 관련 글을 작성하게 되었는데 한번 더 공부하는 기회가 되엇습니다.&lt;br /&gt;puppeteer에서 제공하는 메서드는 제가 다룬 메서드들 이외에도 엄청나게 많고 여러가지 작업에 활용하기 좋기도하고 구글에서 만들어서 지속적으로 업데이트가 진행중이니 처음이신 분들은 한번 학습해보는걸 완전 추천드립니다  &lt;br /&gt;&lt;br /&gt;특히 저는 사이드 프로젝트를 시작할 때 더미데이터를 수집할 때 용이하게 사용하고 있습니다!&lt;/p&gt;</description>
      <category>etc</category>
      <category>Chromium</category>
      <category>Crawler</category>
      <category>node.js</category>
      <category>puppeteer</category>
      <category>scraping</category>
      <category>testing</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/181</guid>
      <comments>https://coding-lks.tistory.com/181#entry181comment</comments>
      <pubDate>Sun, 12 Mar 2023 21:59:43 +0900</pubDate>
    </item>
    <item>
      <title>[DB] 인덱스(Index)</title>
      <link>https://coding-lks.tistory.com/180</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;인덱스(Index)란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DBMS에서 조회(select)의 성능을 향상시키기 위한 목적으로 사용되는 자료구조입니다.&lt;br /&gt;index를 사용해 특정 컬럼을 기준으로 정렬시켜 더 빠르게 특정 데이터에 대한 조회를 더 빠르게 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;인덱스의 특징&lt;/h3&gt;
&lt;ol 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;Index가 존재하기 위한 공간을 필요로한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Page란?&lt;br /&gt;데이터가 저장되는 단위&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;인덱스는 어떤 자료구조로 구성 되어있을까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스는 일반적으로 B-tree 자료구조를 기반으로 구성됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://velog.velcdn.com/images%2Femplam27%2Fpost%2Fbcbce100-d475-4cda-aebe-946d1813949c%2FB%ED%94%8C%EB%9F%AC%EC%8A%A4%20%ED%8A%B8%EB%A6%AC%20%EA%B8%B0%EB%B3%B8%20%ED%98%95%ED%83%9C.jpg&quot; alt=&quot;b+tree 예시&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이지의 종류는 depth에 따라 3가지로 나눌 수 있습니다.&lt;/p&gt;
&lt;ol 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;리프 페이지 - 실제 데이터 페이지 or 데이터의 주소 페이지를 가지고 있음&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DML에 따른 인덱스 관리&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인덱스는 항상 최신의 정렬상태를 유지하는 특징이 있기때문에 쿼리가 수행될 경우 인덱스에 대한 추가적인 작업이 수반됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;select - 추가 작업 없음&lt;/li&gt;
&lt;li&gt;insert - 새로운 데이터로 인해 b-tree의 밸런스가 무너졌을 경우 =&amp;gt; 페이지 분할 작업이 수행됩니다.&lt;/li&gt;
&lt;li&gt;delete - 삭제 작업 시 실제 페이지를 삭제하지 않고 페이지에 대해 사용안함 표시를 합니다. =&amp;gt; 페이지의 낭비를 초래합니다.&lt;/li&gt;
&lt;li&gt;update - insert와 delete에서 일어나는 추가 작업이 일어납니다.페이지 분할이란?&lt;/li&gt;
&lt;li&gt;select를 제외하고는 추가 작업으로 인해 성능에 안좋은 영향을 줄 수 있습니다.&lt;/li&gt;
&lt;/ol&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;DB성능에 악영향을 줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;어떤 상황에 index를 적용해야 할까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 상황에 index를 적용해야 할까?&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;단일 테이블에 인덱스가 많으면 속도가 느려질 수있다. (테이블당 4~5개 권장)&lt;/li&gt;
&lt;li&gt;검색할 데이터가 전체 데이터의 20% 이상이라면, MySQL에서 인덱스를 사용하지 않음. (강제로 사용할 시 성능 저하를 초래할 수 있음)전체 페이지의 대부분을 읽어야 하고, 인덱스 관련 페이지도 읽어야 해서 작업량이 크기 때문이다.&lt;/li&gt;
&lt;li&gt;사용하지 않는 인덱스는 제거하는 것이 바람직함. (실무에서 사용하지 않는 보조 인덱스를 몇개 삭제했을 때 성능이 향상되는 경우도 많음)&lt;/li&gt;
&lt;li&gt;클러스터형 인덱스는 테이블당 하나만 생성할 수 있음&lt;/li&gt;
&lt;li&gt;테이블에 클러스터형 인덱스가 아예 없는 것이 좋은 경우도 있음&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>데이터베이스</category>
      <category>Index</category>
      <category>rdbms</category>
      <category>sql</category>
      <category>인덱스</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/180</guid>
      <comments>https://coding-lks.tistory.com/180#entry180comment</comments>
      <pubDate>Tue, 13 Dec 2022 23:12:32 +0900</pubDate>
    </item>
    <item>
      <title>[js] 비동기 함수 에러 핸들링</title>
      <link>https://coding-lks.tistory.com/179</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 js에서 프로미스 문법이 나오게된 이유 중 하나인 콜백함수, 프로미스 에서의 비동기 함수의 에러처리에 대해 정리해보겠습니다 :)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;br /&gt;콜백함수와 비동기 함수의 에러처리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1000ms의 시간이 흐른후 새로 생성한 Error가 catch문법에서 잡히고 에러메시지를 콘솔에 출력하기를 기대하고 작성했습니다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;try {
	setTimeout(() =&amp;gt; {throw new Error('Error!');}, 1000);
} catch (e) {
    console.log('error!!!!')
	console.error('캐치한 에러', e);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 위 코드는 의도대로 실행되지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;의도 대로 실행된다면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같은 결과를 콘솔에 출력할것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1664716404919&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;캐치한 에러 Error: Error!
    at Timeout._onTimeout (/Users/test.js:5:20)
    at listOnTimeout (internal/timers.js:557:17)
    at processTimers (internal/timers.js:500:7)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의도와 다르게 실행되는 이유는 js에서의 비동기 함수와 콜백함수의 동작과정에 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드의 동작과정을 아래 그림과 함께 살펴보면 4단계로 나눌 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;11111111.png&quot; data-origin-width=&quot;1262&quot; data-origin-height=&quot;756&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DlfNW/btrNwF9zVGM/evAAmiGzh1R3tBKalo5LhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DlfNW/btrNwF9zVGM/evAAmiGzh1R3tBKalo5LhK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DlfNW/btrNwF9zVGM/evAAmiGzh1R3tBKalo5LhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDlfNW%2FbtrNwF9zVGM%2FevAAmiGzh1R3tBKalo5LhK%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;528&quot; height=&quot;316&quot; data-filename=&quot;11111111.png&quot; data-origin-width=&quot;1262&quot; data-origin-height=&quot;756&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 비동기 함수인 setTimeout이 호출되면 setTimeout 함수의 실행 컨텍스트가 생성되어 콜 스택에 푸시되어 실행&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. setTimeout은 비동기 함수이므로 콜백 함수가 호출되는 것을 기다리지 않고 즉시 종료되어 콜 스택에서 제거&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. setTimeout에서 설정된 시간이 지나면 setTimeout함수의 콜백 함수는 태스크 큐로 푸시&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 콜 스택이 비어있을 때 이벤트 루프에 의해 콜 스택으로 푸시되어 실행&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;setTimeout함수의 콜백 함수를 sq라고 하겠습니다.&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;4번 과정에서 &lt;b&gt;sq&lt;/b&gt;가 실행됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 setTimeout함수는 이미 콜 스택에서 제거된 상태이므로 sq를 호출한 것은 setTimeout가 아니라는 의미입니다.&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;그리고 js에서 에러는 호출자 방향으로 전파됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 sq의 에러가 setTimeout함수로 전파되지 않았기 때문에 의도한 대로 동작하지 않았다는 거죠&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;a href=&quot;https://wan2.land/posts/2015/03/09/js-something-2-catchable-settimeout-exception/&quot;&gt;에러핸들링&lt;/a&gt; 예시는 &lt;a href=&quot;https://wan2.land/posts/2015/03/09/js-something-2-catchable-settimeout-exception/&quot;&gt;링크&lt;/a&gt;를 통해 확인해볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프로미스와 비동기 함수의 에러처리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로미스를 사용하면 아래와 같이 reject를 이용하여 에러 핸들링이 가능합니다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const sleep = async function sleep(ms) {
    return new Promise(function(resolve,reject){
        console.log('프로미스 생성중!')
        setTimeout(() =&amp;gt; {
            reject(new Error('async operation failure!'));
        },1000)
    })
  }

async function test() {
	console.log('aa');
	await sleep(3000)
    .catch(e =&amp;gt; {
        console.error('캐치한 에러', e);
    });
	console.log('bb');
}

test()
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드와 콜백함수를 사용한 에러처리 예시 링크의 코드를 비교해보면&amp;nbsp;&lt;br /&gt;Promise문법에 비해 많은 함수와 문법을 사용해야해 간결하지 못한 코드를 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>javascript</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/179</guid>
      <comments>https://coding-lks.tistory.com/179#entry179comment</comments>
      <pubDate>Sun, 2 Oct 2022 22:10:59 +0900</pubDate>
    </item>
    <item>
      <title>OCP(개방 폐쇄 원칙)</title>
      <link>https://coding-lks.tistory.com/178</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;서론&lt;/h3&gt;
&lt;figure id=&quot;og_1662286565975&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;book&quot; data-og-title=&quot;객체 지향과 디자인 패턴 - YES24&quot; data-og-description=&quot;객체 지향 안내서. 객체, 책임, 의존, 캡슐화 등 객체 지향의 주요 개념들을 쉬운 예제와 그림을 통해 이해하기 쉽게 설명한다.&quot; data-og-host=&quot;www.yes24.com&quot; data-og-source-url=&quot;http://www.yes24.com/Product/Goods/9179120&quot; data-og-url=&quot;http://www.yes24.com/Product/Goods/9179120&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bUVBLD/hyPF9TWTVu/qlPivGNDz0UlmqId8bSGzk/img.jpg?width=307&amp;amp;height=400&amp;amp;face=0_0_307_400,https://scrap.kakaocdn.net/dn/GdFaV/hyPHmqBBk1/JLBe9XEXyeIg2fCqOLlgRk/img.jpg?width=307&amp;amp;height=400&amp;amp;face=0_0_307_400,https://scrap.kakaocdn.net/dn/ExxlJ/hyPHe0pR31/AUEbDbTPsbdFqNXTpfF461/img.jpg?width=307&amp;amp;height=400&amp;amp;face=0_0_307_400&quot;&gt;&lt;a href=&quot;http://www.yes24.com/Product/Goods/9179120&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;http://www.yes24.com/Product/Goods/9179120&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bUVBLD/hyPF9TWTVu/qlPivGNDz0UlmqId8bSGzk/img.jpg?width=307&amp;amp;height=400&amp;amp;face=0_0_307_400,https://scrap.kakaocdn.net/dn/GdFaV/hyPHmqBBk1/JLBe9XEXyeIg2fCqOLlgRk/img.jpg?width=307&amp;amp;height=400&amp;amp;face=0_0_307_400,https://scrap.kakaocdn.net/dn/ExxlJ/hyPHe0pR31/AUEbDbTPsbdFqNXTpfF461/img.jpg?width=307&amp;amp;height=400&amp;amp;face=0_0_307_400');&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;객체 지향과 디자인 패턴 - YES24&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;객체 지향 안내서. 객체, 책임, 의존, 캡슐화 등 객체 지향의 주요 개념들을 쉬운 예제와 그림을 통해 이해하기 쉽게 설명한다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.yes24.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;안녕하세요 이번에는 최근에 읽었던 기술서적 객체 지향과 디자인 패턴의 내용중 OCP(개발 폐쇄 원칙)에 대해 개념을 예제코드들과 함께 정리해보려고 합니다 :)&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;개방-폐쇄 원칙(OCP: Open-Closed Principle)&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-09-04 오후 7.15.23.png&quot; data-origin-width=&quot;254&quot; data-origin-height=&quot;250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WRdur/btrLhYXNSEl/ukkQT3QhKkU4AgI3snDZEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WRdur/btrLhYXNSEl/ukkQT3QhKkU4AgI3snDZEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WRdur/btrLhYXNSEl/ukkQT3QhKkU4AgI3snDZEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWRdur%2FbtrLhYXNSEl%2FukkQT3QhKkU4AgI3snDZEk%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;254&quot; height=&quot;250&quot; data-filename=&quot;스크린샷 2022-09-04 오후 7.15.23.png&quot; data-origin-width=&quot;254&quot; data-origin-height=&quot;250&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 개방 폐쇄 원칙이란 객체지향 개발 5대 원칙 중 하나로서&amp;nbsp; 소프트웨어 엔티티(클래스, 모듈, 함수 등)는 확장에 대해서는 열려 있어야 하지만 변경에 대해서는 닫혀 있어야 한다는 원칙입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 폐쇄 원칙은 곧 기존 코드를 변경하지 않으면서(Close) 기능을 추가(Open)할 수 있도록 설계가 되어야 한다는 원칙입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 원칙을 지키기 위해서 보편적으로 추상화와 다형성을 활용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래에서 예제코드와 함께 살펴보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;OCP 적용 전&lt;/h4&gt;
&lt;pre class=&quot;pf&quot;&gt;&lt;code&gt;class User {
    readonly userId: string

    constructor(userId: string){
        console.log(userId)

        this.userId = userId
    }
        
    toStr(): string{
        return `User(userId=${this.userId})`
    }
}

class Account {
    loginKakao(user: User){
        console.log(`Login [userId=${user.userId}]`)
    }

    logoutKakao(user: User){
        console.log(`Logout [userId=${user.userId}]`)
    }

    loginNaver(user: User){
        console.log(`Login [userId=${user.userId}]`)
    }

    logoutNaver(user: User){
        console.log(`Logout [userId=${user.userId}]`)
    }
}

const user = new User('lks')
const account = new Account()
account.loginKakao(user)
account.logoutKakao(user)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 이런 구조로 소프트웨어를 설계했다면 더많은 oauth(google 등등)를 사용해야 할 때&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Account 클래스를 수정해야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;곧 Account클래스가 확장과 수정 모두에 대해 열려있다는 것을 뜻하므로 OCP 원칙을 위반하는 코드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수정엔 닫혀있는 코드를 만들기 위해 OCP원칙을 지키는 코드를 만들어 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;OCP 적용 후&lt;/h4&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;class User {
    readonly userId: string

    constructor(userId: string){
        console.log(userId)

        this.userId = userId
    }
        
    toStr(): string{
        return `User(userId=${this.userId})`
    }
}

abstract class Account {
    public abstract login(user: User): void;
    public abstract logout(user: User): void;

}

class KakaoAuth extends Account {
    public login(user: User){
        console.log(`Login [userId=${user.userId}]`)
    }

    public logout(user: User){
        console.log(`Logout [userId=${user.userId}]`)
    }
}

class NaverAuth extends Account {
    public login(user: User){
        console.log(`Login [userId=${user.userId}]`)
    }

    public logout(user: User){
        console.log(`Logout [userId=${user.userId}]`)
    }
}

const user = new User('lks')
const account = new NaverAuth()
account.login(user)
account.logout(user)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;login(), logout 추상 메서드를 가지고 있는 Account 추상 클래스를 정의하고, 실제 로그인 로그아웃 로직을 구현할 KakaoAuth, NaverAuth 클래스(Account 클래스를 상속)를 정의합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OCP를 위반하는 코드일 때는 구글 로그인 로그아웃을 구현해야한다면 Account에 새로운 클래스를 만들고 호출하는 곳에서의 변경도 불가피했습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;const user = new User('lks')
const account = new Account()
account.loginKakao(user)
account.logoutKakao(user)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;const user = new User('lks')
const account = new Account()
account.loginGoogle(user)
account.logoutGoogle(user)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식으로 Account에 새로운 메서드를 정의하는 것은 물론 메서드가 호출되는 부분들은 모두 수정해야 했죠&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;이제는 Account를 상속하는 GoogleAuth 클래스만 구현하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;class GoogleAuth extends Account {
    public login(user: User){
        console.log(`Login [userId=${user.userId}]`)
    }

    public logout(user: User){
        console.log(`Logout [userId=${user.userId}]`)
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 GoogleAuth 클래스를 사용하여 User의 로그인 로그아웃을 구현할 수 있게됩니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;const user = new User('lks')
const account = new GoogleAuth()
account.login(user)
account.logout(user)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 다형성을 사용한다면 Account클래스는 수정 없이 새로운 OAuth를 추가할 수 있게 되어 OCP를 따르게 할 수 있습니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;정리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OCP는 해당 클래스의 기존 동작을 변경하지 않고(코드의 변경에 닫혀있어야 하고)&amp;nbsp;클래스의 동작을 확장(확장에 열려있어야)하는 것을 목표로&amp;nbsp;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 유지 관리 및 수정이 쉬운 코드를&amp;nbsp;작성하는데&amp;nbsp;필수적입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 개인적으로 OOP의 원칙 중 비교적 떠올리기, 적용하기 쉬운 원칙이라고 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OCP가 코드의 재사용성을 높이고 유지보수를 용이하게 만들지만 과도하게 적용할 시에는 설계가 필요이상으로 복잡해질 수 있다는 점을 유의하고 적용하면 좋겠습니다 :)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>oop</category>
      <category>OCP</category>
      <category>OOP</category>
      <category>typescript</category>
      <category>개방 폐쇄 원칙</category>
      <category>객체 지향 설계</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/178</guid>
      <comments>https://coding-lks.tistory.com/178#entry178comment</comments>
      <pubDate>Sun, 4 Sep 2022 19:14:39 +0900</pubDate>
    </item>
    <item>
      <title>auth 보일러 플레이트 개발 - 1</title>
      <link>https://coding-lks.tistory.com/177</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요 오늘은 저번 포스팅에서도 다루었고 대부분의 서비스에서 필수적인 기능인 인증/인가에 대한 보일러 플레이트를 개발해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이드 프로젝트를 하면서 인증/인가에 대해서는 나만의 보일러플레이트 코드를 개발해놔야겠다생각했었는데 이제야 개발하게 됬습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 해당 포스팅에서는&lt;/p&gt;
&lt;ol 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;jwt기반의 인증/인가 프로젝트를 개발합니다.&lt;/li&gt;
&lt;li&gt;nest, typescript, mysql를 사용하여 개발합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;병아리 개발자에게 많은 조언 부탁드립니다! :)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnMbxW/btrKbtc1s1J/kA4906AO8AkNi5m4eZfg2k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnMbxW/btrKbtc1s1J/kA4906AO8AkNi5m4eZfg2k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnMbxW/btrKbtc1s1J/kA4906AO8AkNi5m4eZfg2k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnMbxW%2FbtrKbtc1s1J%2FkA4906AO8AkNi5m4eZfg2k%2Fimg.jpg&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;496&quot; height=&quot;372&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;http 통신 인증에서의 암호화&lt;br /&gt;&lt;br /&gt;http 통신시에 password를 암호화하기 위해 RSA암호화를 사용합니다.또한 password를 복호화 해야하기때문에 양방향 암호화 알고리즘을 사용합니다 ex) rsa알고리즘&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. Entity 정의하기&lt;/h4&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;CREATE TABLE user(
user_id NVARCHAR(20) NOT NULL,
password char(60) NOT NULL,
role NVARCHAR(20) NOT NULL default basicuser,
CONSTRAINT admin PRIMARY KEY(user_id, system_name))
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 user Entity를 정의했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 프로젝트에서 필수적으로 있어야하는 user_id, password, role컬럼을 가지게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 프로젝트는 보일러플레이트 프로젝트이기때문에 user테이블에서 필수적인 컬럼만 가지도록 설계했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. POST 회원가입(signup) 구현하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회원가입(signup) 구현에서는 먼저&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;userId, password, role데이터를 수신합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회원가입을 위한 DTO를 정의했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최소한의 유효성만을 체크하고 있습니다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;import { IsAlphanumeric, IsNotEmpty, Length } from 'class-validator';

export class CreateUserDto {
  @IsNotEmpty()
  @IsAlphanumeric()
  @Length(10, 15)
  userId: string;

  @IsNotEmpty()
  password: string;

  @IsNotEmpty()
  role: string;
}
&lt;/code&gt;&lt;/pre&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;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;회원기능을 담당하는 Account 클래스의 멤버변수를 초기화합니다.&lt;/li&gt;
&lt;li&gt;DB에 password를 저장하기 위해 단방향 암호화를 수행합니다.&lt;/li&gt;
&lt;li&gt;암호화된 password와 회원 정보를 DB에 저장합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;async createAccount(createUserDto: CreateUserDto): Promise&amp;lt;User&amp;gt; {
        const account = new Account();
        account.setSignUpinfo(createUserDto);
        account.setHashPw()
        const newUser = await this.userRepository.createUser(account)
    return newUser;
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 Account class코드입니다.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;import { CreateUserDto } from &quot;./dto/create-user.dto&quot;;
import { SignInUserDto } from &quot;./dto/signIn-user.dto&quot;;
import { tLoginRes } from &quot;./dto/types&quot;;
import * as bcrypt from 'bcrypt'
import * as jwt from 'jsonwebtoken';

export class Account {
    userId: string;
    private password: string;
    hashedPw: string;
    role: string;
    saltRounds = 10;

    setSignUpinfo(createUserDto: CreateUserDto){
        this.password = createUserDto.password
        this.role = createUserDto.role;
        this.userId = createUserDto.userId;
    }

    setHashPw(): boolean {
        const salt = bcrypt.genSaltSync(this.saltRounds);
        this.hashedPw = bcrypt.hashSync(this.password, salt);
        return true
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 클래스를 작성하면서 평문 pw , hashedPw(암호화된 pw)를 둘 다 멤버변수로 정희하다보니 네이밍이 혼동이 될 수도 있겠다는 생각이 자꾸 들었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hashedPw 멤버변수는 명확한데 password 멤버변수는 의미가 명확하지 않다는 느낌이 강하게 들어 더 고민을 해봐야할 것 같네요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.&amp;nbsp; GET 로그인 API&lt;/h4&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;import { IsAlphanumeric, IsNotEmpty, Length } from 'class-validator';

export class SignInUserDto {
  @IsNotEmpty()
  @IsAlphanumeric()
  @Length(10, 15)
  userId: string;

  @IsNotEmpty()
  password: string;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회원가입을 위한 DTO를 정의했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인이기때문에 id와 password만을 수신합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;async signIn(signInUserDto: SignInUserDto): Promise&amp;lt;tLoginRes&amp;gt; {
        const account = new Account();
        account.setSignInInfo(signInUserDto);
        const user = await this.userRepository.getUserById(account.userId);

        if (!account.comparePassword(user.password))
            throw new BadRequestException('password가 일치하지 않습니다.')
        if (!account.getJwt())
            throw new BadRequestException('token발급에 실패했습니다.')

    return account.getResform();
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회원가입 비즈니스 로직을 담당하는 메소드는 UserService.signIn()으로 이름을 정했습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 user가 존재하는지 DB를 조회&lt;/li&gt;
&lt;li&gt;수신한 password가 DB에 저장된 user의 password와 일치하는지 확인&lt;/li&gt;
&lt;li&gt;userId값을 가지고 있는 jwt를 발급&lt;/li&gt;
&lt;li&gt;클라이언트 단에 데이터 송신&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot;&gt;&lt;code&gt;import { CreateUserDto } from &quot;./dto/create-user.dto&quot;;
import { SignInUserDto } from &quot;./dto/signIn-user.dto&quot;;
import { tLoginRes } from &quot;./dto/types&quot;;
import * as bcrypt from 'bcrypt'
import * as jwt from 'jsonwebtoken';

export class Account {
    userId: string;
    private password: string;
    hashedPw: string;
    role: string;
    saltRounds = 10;
    token: string;

    setSignUpinfo(createUserDto: CreateUserDto){
        this.password = createUserDto.password
        this.role = createUserDto.role;
        this.userId = createUserDto.userId;
    }

    setHashPw(): boolean {
        const salt = bcrypt.genSaltSync(this.saltRounds);
        this.hashedPw = bcrypt.hashSync(this.password, salt);
        return true
    }

    setSignInInfo(signInUserDto: SignInUserDto){
        this.password = signInUserDto.password
        this.userId = signInUserDto.userId;
    }
    
    comparePassword(DbPw: string): boolean {
        return bcrypt.compareSync(this.password, DbPw);
    }

    getJwt(): boolean {
        const token = jwt.sign(
            {
                type: 'JWT',
                id: this.userId,
            },
            process.env.SECRET_KEY,
            {
                expiresIn: '60m',
                issuer: 'admin',
            }
        );
        this.token = token
        return true
    };

    getResform(): tLoginRes{
        return {
            userId: this.password,
            token: this.token
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;account class에 4개의 메소드가 추가되었습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로그인을 위해 멤버변수를 초기화하는 setSignInInfo 메소드&lt;/li&gt;
&lt;li&gt;password가 유효한지 확인하는 comparePassword 메소드&lt;/li&gt;
&lt;li&gt;인증/인가를 위한 jwt를 발급하는 getJwt 메소드&lt;/li&gt;
&lt;li&gt;응답을 위한 형식의 object를 반환하는 getResform 메소드입니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;정리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보일러 플레이트를 개발하면서 아래와 같은 것들이 애매하고 궁금해지는 시간이었습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;nest에서 타입을 정의하여 사용할 때 어떤 디렉토리에 격리하여 사용하는지&lt;/li&gt;
&lt;li&gt;내가 사용한 Account와 같은 class를 정의하여 사용할 때 어떤 디렉토리에 격리하는게 좋을지&lt;/li&gt;
&lt;li&gt;account class를 어떻게 추상화, 캡슐화할 수 있을지&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;++네이밍은 항상 어렵군요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서의 업무에서는 프로젝트에서 사용하는 기술스택을 하나도 사용하지 않다보니 프로젝트를 진행할 수록 고민이 많아지게 됩니다ㅜ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 현재는 아래와 같은 Access Token만을 사용하는 프로세스이지만&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;398&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sKInJ/btrKcv2L9Vo/R56XfDkSFnnPEW7CBgKca1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sKInJ/btrKcv2L9Vo/R56XfDkSFnnPEW7CBgKca1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sKInJ/btrKcv2L9Vo/R56XfDkSFnnPEW7CBgKca1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsKInJ%2FbtrKcv2L9Vo%2FR56XfDkSFnnPEW7CBgKca1%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;624&quot; height=&quot;303&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;398&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로는 아래와 같이 Refresh Token + Access Token 기반의 프로세스를 가진 프로젝트를 개발할 예정입니다 :)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-08-21 오후 11.41.33.png&quot; data-origin-width=&quot;1652&quot; data-origin-height=&quot;960&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqdQds/btrKhlx6sfY/HQfiaHrykfdbC2pirKKaOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqdQds/btrKhlx6sfY/HQfiaHrykfdbC2pirKKaOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqdQds/btrKhlx6sfY/HQfiaHrykfdbC2pirKKaOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqdQds%2FbtrKhlx6sfY%2FHQfiaHrykfdbC2pirKKaOk%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;621&quot; height=&quot;361&quot; data-filename=&quot;스크린샷 2022-08-21 오후 11.41.33.png&quot; data-origin-width=&quot;1652&quot; data-origin-height=&quot;960&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 글은 시리즈로 작성할 예정이며 아래는 github repository 링크입니다.&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;figure id=&quot;og_1661093031265&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;GitHub - Lks9172/auth_boilerplate&quot; data-og-description=&quot;Contribute to Lks9172/auth_boilerplate development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/Lks9172/auth_boilerplate&quot; data-og-url=&quot;https://github.com/Lks9172/auth_boilerplate&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ccMjsN/hyPxovE2uo/d6HNjYSxOH0v4q1Xsokolk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/Lks9172/auth_boilerplate&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/Lks9172/auth_boilerplate&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ccMjsN/hyPxovE2uo/d6HNjYSxOH0v4q1Xsokolk/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;GitHub - Lks9172/auth_boilerplate&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to Lks9172/auth_boilerplate development by creating an account on GitHub.&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>
      <category>auth</category>
      <category>boilerplate</category>
      <category>jwt</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/177</guid>
      <comments>https://coding-lks.tistory.com/177#entry177comment</comments>
      <pubDate>Sun, 21 Aug 2022 23:46:43 +0900</pubDate>
    </item>
    <item>
      <title>인증과 인가</title>
      <link>https://coding-lks.tistory.com/176</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;인증과 인가&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;보통 sever or client 개발을 학습하거나 어떤 서비스를 개발한다고 할때 가장 처음, 많이 구현하고 학습하는 요소&lt;/li&gt;
&lt;li&gt;인증(Authentication)과 인가(Authorization)는 항상 함께 등장하는 개념이면서 사용하기에 헷갈리는 용어&lt;/li&gt;
&lt;li&gt;누가, 언제, 어떻게 쓰고 있는가를 파악하기 위해 어떤 사이트에서든 인증, 인가가 있음&lt;/li&gt;
&lt;li&gt;인증과 인가는 API에서 가장 자주 구현되는 기능중 하나.&lt;/li&gt;
&lt;li&gt;문맥상 어색하지 않은 경우가 있어 이 둘의 차이를 두지 않고 사용하는 경우도 빈번합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 인증이란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유저가 누구인지 확인하는 절차, 회원가입하고 로그인 하는 것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증은 어떤 개체(사용자 또는 장치)의 신원을 확인하는 과정입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&amp;nbsp; &amp;nbsp; ex) 은행의 경우 은행에서 업무를 보려면 신원을 확인하기 위해 신분증을 제시합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;온라인에서는 보통 ID와 paassword를 입력하여 인증을 수행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹은 회원가입할 때 sms로 전달받은 코드를 입력하는 경우도 회원가입을 위한 인증을 수행하는 경우 중 하나라고 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2 .인가란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유저에게 특정 리소스에 대한 권한을 허락하는 것.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;ex) 공연장에 입장하기 위해 티켓을 구매하고 제시하는 경우 이 경우 공연기획사에선 여러분의 티켓을 통해 공연장에 입장할 권한이 있는지,&amp;nbsp;어떤 좌석에 앉을 권한이 있는지 여부를 검사하죠!&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증과 달리 인가는 어떤 개체가 어떤 리소스에 접근할 수 있는지 또는 어떤 동작을 수행할 수 있는지를 검증하는 것, 즉 접근 권한을 얻는 일을 말합니다.&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;서버개발 관점에서 보통 token(jwt기반의 경우)을 통해 해당 유저가 해당 리소스에 권한이 있는지 없는지 여부를 판단하여 인가를 다룹니다.(해당 리소스에 대한 접근 권한 여부를 확인)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증과 인가에 대해 위에서 예를 들었듯이 은행, 공연장, 공항 입출국 등을 예시로 들을 때 실생활에서는 인증과 인가가 대부분 함께 일어나는 요소이기 때문에 혼란의 원인이 된다고 생각이 듭니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;ex) 회사 사무실에 출입할 때 사원증을 사용하여 출입을 하는데, 보통 출입증을 대면 인증과 인가가 한꺼번에 일어난다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함께 일하는 인원들과 소통을 원활하게 하려면 용어에 대한 의미를 확실히 인지하고 잇어야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증과 인가는 자주 중복해서 사용되며, 저 또한 처음 학습할 때 자주 의미를 헷갈리곤 해서 두 용어의 의미를 정리해보았습니다  &lt;/p&gt;</description>
      <category>etc</category>
      <category>authentication</category>
      <category>authorization</category>
      <category>인가</category>
      <category>인증</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/176</guid>
      <comments>https://coding-lks.tistory.com/176#entry176comment</comments>
      <pubDate>Sun, 24 Jul 2022 19:20:01 +0900</pubDate>
    </item>
    <item>
      <title>[js] 콜백 큐의 종류에 따른 우선순위!</title>
      <link>https://coding-lks.tistory.com/175</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Microtask Queue 는 Task Queue 보다 우선순위가 높다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  먼저, 일반적인 스크립트 (ex: console.log ) 가 Task Queue 에 들어갔다가 Call Stack 에서 실행됨. 그 다음 비동기 함수들이 백그라운드를 거쳐 각 함수에 알맞는 큐 (Task Queue Microtask Queue) 에 들어가게 됨.&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;  Macro Task Queue 는 Task Queue 와 동일한 용어이고, PromiseJobs Queue 는 Microtask Queue 와 동일한 용어임.&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;Microtask Queue 는 일반적인 Task Queue 보다 더 높은 우선순위를 가지고 있음.&lt;/li&gt;
&lt;li&gt;그렇기 때문에 Task Queue 에 대기중인 것이 있다고 하더라도 Microtask Queue 에 있는 것이 우선해서 실행됨.&lt;/li&gt;
&lt;li&gt;이 때문에 Microtask Queue 에서 시간이 오래 걸릴 경우, 렌더링이 느려지는 현상이 발생할 수도 있음. &amp;rarr; 렌더링 엔진은 일반 태스크에 있으므로 작동하지 못하기 때문임!&lt;/li&gt;
&lt;li&gt;결과적으로, 이벤트 루프는 우선적으로 Microtask Queue 를 확인함. 만약 Microtask Queue 에 콜백이 있다면, 이를 먼저 Call Stack 에 담음. ******그리고 Mircotask Queue 에 더 이상 처리해야할 콜백이 없다면, Task Queue 를 확인 후 처리하는 방식으로 이루어짐.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Task Queue 와 Microtask Queue 의 차이&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2개의 큐 모두 콜백 함수가 들어간다는 점에서 동일함.&lt;/li&gt;
&lt;li&gt;단, 어떤 함수를 실행하느냐에 따라 어디로 들어가는지가 달라짐.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Task Queue 에 넣는 함수
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;setTimeout setInterval setImmediate requestAnimationFrame I/O UI Rendering&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Microtask Queue 에 넣는 함수
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;process.nextTick Promise Object.observe MutationObserver&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;마이크로태스트 큐는 태스크 큐와는 별도의 큐이며 마이크로태스크 큐에는 프로미스의 후속 처리 메서드의 콜백 함수가 일시 저장됨. 그 외의 비동기 함수의 콜백 함수나 이벤트 핸들러는 태스크 큐에 일시 저장됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이벤트 루프 중 어떤 시점에서 런타임은 대기열에서 &lt;b&gt;가장 오래된 메시지부터&lt;/b&gt; 처리하기 시작한다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Callback Queue 란?&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자바스크립트 런타임은 처리할 메시지 목록인 메시지 대기열을 사용하는데, 이를 Callback Queue 라고 함.&lt;/li&gt;
&lt;li&gt;각 메시지에는 메시지를 처리하기 위해 호출되는 관련 함수가 있음.&lt;/li&gt;
&lt;li&gt;Callback Queue 의 내부에는 여러 가지의 큐들이 담겨있으며, 대표적인 큐로는 Microtask Queue , Animation Frames , Task Queue 가 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Callback Queue 함수 호출 방식&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;495&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MXHjD/btrGS4hg4i3/IqIGcZtJ7Vhn1rO69GPk31/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MXHjD/btrGS4hg4i3/IqIGcZtJ7Vhn1rO69GPk31/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MXHjD/btrGS4hg4i3/IqIGcZtJ7Vhn1rO69GPk31/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/MXHjD/btrGS4hg4i3/IqIGcZtJ7Vhn1rO69GPk31/img.gif&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;880&quot; height=&quot;495&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;495&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 선입선출 (FIFO) 방식을 따르기 때문에 대기열에서 가장 오래된 메시지부터 처리함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;setTimeout 에 인자로 넘겨진 콜백 함수는 실제로 0ms 가 아닌 최소 4ms 후에 실행된다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;아래 코드의 출력 순서는 B &amp;rarr; C &amp;rarr; A 임.&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;setTimeout(function() { 
		// (A)
    console.log('A');
}, 0);
Promise.resolve().then(function() { 
		// (B)
    console.log('B');
}).then(function() { 
		// (C)
    console.log('C');
});
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Promise 인 B, C 가 setTimeout 보다 먼저 실행됨.&lt;/li&gt;
&lt;li&gt;setTimeout 인 A 가 일반 태스크 큐를 사용하는 반면, Promise 인 B 는 마이크로 태스크 큐를 사용함. &amp;rarr; 마이크로 태스크 큐는 일반 태스크 큐보다 더 높은 우선순위를 갖기 때문에 태스크 큐에 먼저 대기중인 태스크가 있더라도 마이크로 태스크가 먼저 실행됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;그렇다면 setTimeout 의 호출 시간은 왜 다를까?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;브라우저 환경에서 실제 대기 시간은 0이 아닙니다.&lt;/b&gt; 브라우저는&amp;nbsp;&lt;a href=&quot;https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers&quot;&gt;HTML5 표준&lt;/a&gt;에서 정한 중첩 타이머 실행 간격 관련 제약을 준수합니다. 해당 표준엔 &quot;다섯 번째 중첩 타이머 이후엔 대기 시간을 최소 4밀리초 이상으로 강제해야 한다.&quot;라는 제약이 명시되어있습니다.&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;위의 A 는 4ms 이후 실행된 것으로 예측할 수 있음.&lt;/li&gt;
&lt;li&gt;브라우저는 내부적으로 타이머의 최소단위(Tick)을 정하여 관리하기 때문에, 실제로는 그 최소단위만큼 지난 후에 태스크 큐에 추가되게 됨.&lt;/li&gt;
&lt;li&gt;이 최소단위는 브라우저 별로 조금씩 다른데, 크롬 브라우저의 경우 최소단위로 4ms를 사용하기 때문에 크롬에서 setTimeout(fn, 0) 은 setTimeout(fn, 4) 와 동일한 의미를 갖게 됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>javascript</category>
      <category>js</category>
      <category>node.js</category>
      <category>브라우저</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/175</guid>
      <comments>https://coding-lks.tistory.com/175#entry175comment</comments>
      <pubDate>Sun, 10 Jul 2022 23:52:13 +0900</pubDate>
    </item>
    <item>
      <title>[js] js는 싱글스레드언어인데 어떻게 병렬처리가 가능한걸까???</title>
      <link>https://coding-lks.tistory.com/174</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요 오늘의 포스팅은 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;자바스크립트는 정말 싱글스레드로 동작하는가?&lt;/span&gt;에 대해서 포스팅하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트로 개발을 하던중 자바스크립트는 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;비동기 함수 or Promise.all등의 문법으로 라이브러리나 패키지가 아닌 언어수준에서 병렬처리가 가능한대 어떻게 싱글스레드 언어로 분류되는지&lt;/span&gt;에 대한 의문이 생겨 오늘의 포스팅을 작성하게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트는 싱글스레드 언어라고 알려져있는데 어떻게 언어수준에서 병렬처리를 제공하는지에 대해 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 기반지식으로 이벤트루프란 무엇인지에 대해 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이벤트 루프란?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자바스크립트 엔진이 아닌, 런타임(browser, node.js)에서 가지고 있는 하나의 장치입니다.&lt;/li&gt;
&lt;li&gt;콜 스택과 태스크 큐(콜백 큐)를 감시하며, 콜 스택이 비어있는 경우에 태스크 큐에서 태스크를 가져와 콜 스택에 넣어 주는 동작을 합니다.&lt;/li&gt;
&lt;li&gt;&amp;rarr; 자바스크립트는 싱글스레드 언어이기 때문에 한번에 하나씩 실행되지만, &lt;span style=&quot;color: #ef5369;&quot;&gt;Web API&lt;/span&gt;, &lt;span style=&quot;color: #ef5369;&quot;&gt;Callback Queue&lt;/span&gt;, &lt;span style=&quot;color: #ef5369;&quot;&gt;Event Loop&lt;/span&gt; 로 인하여 멀티스레드처럼 보입니다.&lt;/li&gt;
&lt;li&gt;태스크가 들어오길 기다렸다가 태스크가 들어오면 이를 처리하고, 처리할 태스크가 없는 경우에는 대기하는 형태입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이벤트 루프는 큐의 다음 메시지를 처리한다는 의미이다.&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이벤트 루프는 그 구현 방식 때문에 붙은 이름으로 아래와 동작과정이 유사하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1654179391580&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;while(queue.waitForMessage()){
  queue.processNextMessage();
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #eb5757;&quot;&gt;queue.&lt;span style=&quot;background-color: #ffffff; color: #eb5757;&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;waitForMessage()&lt;span style=&quot;color: #000000;&quot;&gt; 함수는 현재 아무 메시지도 없다면 새로운 메시지 도착을 동기적으로 기다림.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&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;이벤트 루프가 동작하려면 2가지 조건이 충족되어야 합니다.&lt;/li&gt;
&lt;li&gt;아래의 두 조건이 충족될 때&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;Callback Queue&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;에 있는 함수들을 순서대로&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Call Stack&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;으로 이동시켜 해당 함수가 호출되도록 함.&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;Callback Queue&lt;/span&gt; 에 실행해야 할 함수가 있다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;Call Stack&lt;/span&gt; 이 비어있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;자바스크립트는 현재 위치한 곳의 함수들의 호출이 끝나야만 다음 작업을 실행한다.&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예를 들어, &lt;span style=&quot;color: #ef5369;&quot;&gt;setTimeout&lt;/span&gt; 에 3초의 시간을 두고 함수를 실행하고자 할 때 3초 후 &lt;span style=&quot;color: #ef5369;&quot;&gt;Callback Queue&lt;/span&gt; 로 넘어가긴 하겠지만, 그 사이 &lt;span style=&quot;color: #ef5369;&quot;&gt;Call Stack&lt;/span&gt; 이 비어지지 않는다면 &lt;span style=&quot;color: #ef5369;&quot;&gt;Event Loop&lt;/span&gt; 는 동작하지 않을 것입니다.&lt;/li&gt;
&lt;li&gt;&amp;rarr; 즉, &lt;span style=&quot;color: #ef5369;&quot;&gt;setTimeout&lt;/span&gt; 으로 걸어둔 함수가 반드시 3초 후에 실행될거라는 보장이 없다는 뜻이기도 합니다.&lt;/li&gt;
&lt;li&gt;나아가 &lt;span style=&quot;color: #ef5369;&quot;&gt;Event Loop&lt;/span&gt; 는 &lt;span style=&quot;color: #ef5369;&quot;&gt;Callback Queue&lt;/span&gt; 뿐 아니라 &lt;span style=&quot;color: #ef5369;&quot;&gt;render queue&lt;/span&gt;, &lt;span style=&quot;color: #ef5369;&quot;&gt;job queue&lt;/span&gt; 등 여러가지 큐를 관리하고 실행하기 때문에 유념해야할 점은 내가 원하는 정확한 시점에 정확히 &lt;span style=&quot;color: #ef5369;&quot;&gt;Event Loop&lt;/span&gt; 가 동작하여 함수를 실행하지 않을수도 있다는 것 입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;자바스크립트가 싱글 스레드인 이유는 콜 스택이 하나이기 때문이다.&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자바스크립트는 콜 스택이라는 자료 구조를 이용해서 명령문을 실행하는데, 하나의 스레드에서 하나의 콜 스택으로 모든 명령문을 순차적으로 실행하기 때문에 싱글스레드 언어라고 할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&amp;rarr; 이 말은 즉, &lt;span style=&quot;background-color: #9feec3;&quot;&gt;한 번에 하나의 태스크(작업)만 처리가 가능하다는 뜻&lt;/span&gt;임. (하나의 함수가 실행되면 이 함수의 실행이 끝날 때까지 어떤 작업도 중간에 끼어들지 못함)&lt;/li&gt;
&lt;li&gt;브라우저는 탭마다 하나의 스레드를 생성하며, 프로그램은 이 메인 스레드(싱글 스레드)에서 작동함.그렇기 때문에 이벤트 루프가 동작하는 스레드가 메인 스레드라고 볼 수 있습니다.&lt;/li&gt;
&lt;li&gt;&amp;rarr; 자바스크립트를 실행시키는 스레드는 단 하나뿐이며, 이 스레드가 바로 이벤트 루프가 실행되는 스레드입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;905&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/A8rSC/btrDPB2XQ3S/OOJW0GWTc6wno5ySJQ20c0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/A8rSC/btrDPB2XQ3S/OOJW0GWTc6wno5ySJQ20c0/img.jpg&quot; data-alt=&quot;js 런타임(browser, node.js)의 구조를 도식화한 그림&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A8rSC/btrDPB2XQ3S/OOJW0GWTc6wno5ySJQ20c0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA8rSC%2FbtrDPB2XQ3S%2FOOJW0GWTc6wno5ySJQ20c0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;905&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;905&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;js 런타임(browser, node.js)의 구조를 도식화한 그림&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;자바스크립트 런타임이란?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로그래밍 언어가 구동되는 환경으로, 쉽게 말해 프로그램을 뜻합니다. &amp;rarr; 즉, &lt;span style=&quot;background-color: #9feec3;&quot;&gt;런타임이란 &amp;ldquo;어떤 프로그래밍 언어가 동작할 수 있는 프로그램&lt;/span&gt;&quot;입니다.&lt;/li&gt;
&lt;li&gt;자바스크립트의 런타임은 무엇이 있을까? Browser or Node.js 가 대표적인 자바스크립트 런타임입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;자바스크립트는 정말 싱글 스레드일까?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자바스크립트 엔진만 보면 싱글 스레드가 맞지만, 런타임으로 확장해서 보면 멀티 스레드라고 할 수 있습니다.&lt;/li&gt;
&lt;li&gt;브라우저 엔진은 렌더링, 통신 준비, 메모리 초기화, 프로세스 정리 등과 같은 수많은 작업을 백그라운드에서 멀티스레드로 처리합니다.&lt;/li&gt;
&lt;li&gt;자바스크립트 엔진은 콜 스택과 메모리 힙만 담당하고 나머지는 브라우저 영역에서 담당합니다.&amp;rarr; 이처럼 실제로 자바스크립트 엔진은 독립적으로 실행되지 않고, 런타임(웹 &lt;span style=&quot;color: #ef5369;&quot;&gt;Browser&lt;/span&gt; or &lt;span style=&quot;color: #ef5369;&quot;&gt;NodeJS&lt;/span&gt;같은) 멀티 스레드 환경에 임베디드되어 실행됩니다.&lt;/li&gt;
&lt;li&gt;즉, 런타임 영역에서 구동됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심!!!!!!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 결과적으로 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;자바스크립트 자체는 싱글스레드&lt;/span&gt;가 맞지만, 현실적으로 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;자바스크립트 엔진이 독립적으로 실행되지 않기 때문에 자바스크립트와 Web API, 이벤트 루프 등의 런타임 환경(멀티스레드로 동작)&lt;/span&gt;을 분리하여 말하기가 어렵습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;자바스크립트 비동기 동작의 핵심 요소는 런타임(Browser, node.js) 가지고 있다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 그림을 한번 더 소환하겠다!!!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;905&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/F54uk/btrDQd8psi8/7eKK5AGJ2nLkmQ8FGkjXAk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/F54uk/btrDQd8psi8/7eKK5AGJ2nLkmQ8FGkjXAk/img.jpg&quot; data-alt=&quot;js 런타임(browser, node.js)의 구조를 도식화한 그림&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/F54uk/btrDQd8psi8/7eKK5AGJ2nLkmQ8FGkjXAk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FF54uk%2FbtrDQd8psi8%2F7eKK5AGJ2nLkmQ8FGkjXAk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;905&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;905&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;js 런타임(browser, node.js)의 구조를 도식화한 그림&lt;/figcaption&gt;
&lt;/figure&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;span style=&quot;background-color: #9feec3;&quot;&gt;싱글스레드이지만, 이벤트 루프를 통한 비동기 방식으로 동시성을 지원&lt;/span&gt;합니다.&lt;/li&gt;
&lt;li&gt;&amp;rarr; &lt;span style=&quot;color: #ef5369;&quot;&gt;Web API&lt;/span&gt;, &lt;span style=&quot;color: #ef5369;&quot;&gt;Event Table&lt;/span&gt;, &lt;span style=&quot;color: #ef5369;&quot;&gt;Callback Queue&lt;/span&gt;, &lt;span style=&quot;color: #ef5369;&quot;&gt;Event Loop&lt;/span&gt; 덕분에 자바스크립트를 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;멀티 스레드처럼 작동&lt;/span&gt;시킬 수 있습니다.&lt;/li&gt;
&lt;li&gt;비동기 호출을 위해 사용되는 &lt;span style=&quot;color: #ef5369;&quot;&gt;setTimeout&lt;/span&gt;, &lt;span style=&quot;color: #ef5369;&quot;&gt;XMLHttpRequest&lt;/span&gt; 와 같은 함수들은 &lt;span style=&quot;color: #ef5369;&quot;&gt;Web API&lt;/span&gt; 에 정의되어 있습니다.&lt;/li&gt;
&lt;li&gt;&amp;rarr; 당장 실행되지 않는 함수들이 호출되기를 기다리는 공간이라고 볼 수 있으며, 해당 함수가 콜 스택에서 &lt;span style=&quot;color: #ef5369;&quot;&gt;Web API&lt;/span&gt; 로 이동될 때&lt;span style=&quot;color: #ef5369;&quot;&gt; Call Stack&lt;/span&gt; 에서는 호출이 끝난 것으로 간주되어 사라지게 됩니다. (정해진 시간이 지나면 함수 호출을 위해 &lt;span style=&quot;color: #ef5369;&quot;&gt;Callback Queue&lt;/span&gt; 로 이동함)&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;Nods.js&lt;/span&gt;는 비동기 지원을 위해 &lt;span style=&quot;color: #ef5369;&quot;&gt;libuv&lt;/span&gt;라이브러리를 사용하며, 이 &lt;span style=&quot;color: #ef5369;&quot;&gt;linuv&lt;/span&gt; 가 이벤트 루프를 제공함. &amp;rarr; 자바스크립트 엔진은 비동기 작업을 위해 노드js의 api를 호출하며, 이 때 넘겨진 콜백은 &lt;span style=&quot;color: #ef5369;&quot;&gt;libuv&lt;/span&gt;의 이벤트 루프를 통해 스케쥴되고 실행됨. (단, 자바스크립트 엔진의 이벤트 루프와는 다르게 동작함)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;357&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cc2SiS/btrDPjnZrEq/YT4OY1LNhgyCydattZCUPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cc2SiS/btrDPjnZrEq/YT4OY1LNhgyCydattZCUPK/img.png&quot; data-alt=&quot;node.js의 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cc2SiS/btrDPjnZrEq/YT4OY1LNhgyCydattZCUPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcc2SiS%2FbtrDPjnZrEq%2FYT4OY1LNhgyCydattZCUPK%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;728&quot; height=&quot;357&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;357&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;node.js의 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;비동기 함수 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;동작 원리&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;784&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmVGMa/btrDPr0eKsz/gyuFEueMHknEvLsUY8hu01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmVGMa/btrDPr0eKsz/gyuFEueMHknEvLsUY8hu01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmVGMa/btrDPr0eKsz/gyuFEueMHknEvLsUY8hu01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmVGMa%2FbtrDPr0eKsz%2FgyuFEueMHknEvLsUY8hu01%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;748&quot; height=&quot;784&quot; data-origin-width=&quot;748&quot; data-origin-height=&quot;784&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 인터프리터가 비동기 함수를 만나면, 즉시 &lt;span style=&quot;color: #ef5369;&quot;&gt;Call Stack&lt;/span&gt;에서 지워버린다.&lt;br /&gt;2. 이 비동기 함수는 &lt;span style=&quot;color: #ef5369;&quot;&gt;Web API&lt;/span&gt;로 넘어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 비동기 함수는 &lt;span style=&quot;color: #ef5369;&quot;&gt;Web API&lt;/span&gt;에 담겨있다가, 타이머나 로드 등이 완료되면 &lt;span style=&quot;color: #ef5369;&quot;&gt;Callback Queue&lt;/span&gt;로 보내진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. &lt;span style=&quot;color: #ef5369;&quot;&gt;Event Loop&lt;/span&gt;는 &lt;span style=&quot;color: #ef5369;&quot;&gt;Call Stack&lt;/span&gt;과 &lt;span style=&quot;color: #ef5369;&quot;&gt;Callback Queue&lt;/span&gt; 사이에서 &lt;span style=&quot;color: #ef5369;&quot;&gt;Call Stack&lt;/span&gt;이 비어있는지 주시한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 모든 함수의 실행이 완료되고 &lt;span style=&quot;color: #ef5369;&quot;&gt;Call Stack&lt;/span&gt;이 비워지면, &lt;span style=&quot;color: #ef5369;&quot;&gt;Event Loop&lt;/span&gt;는 &lt;span style=&quot;color: #ef5369;&quot;&gt;Callback Queue&lt;/span&gt;에 담겨있는 함수들을 먼저 들어온 순서대로 &lt;span style=&quot;color: #ef5369;&quot;&gt;Call Stack&lt;/span&gt;으로 넘겨준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 내용을 주제로 42seoul에서 js스터디를 할 당시(11개월전) 10시에 만나서 16시에 스터디가 마무리된 기억이 있다.&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;js를 공부한지 얼마 안되었거나 깊게 학습하지 않아 동작원리까지 학습되지 않은 분들이 해당 포스팅을 통해 최소&lt;span style=&quot;background-color: #9feec3;&quot;&gt; js는 싱글스레드 언어이지만 어떻게 async문법 or Promise.all 문법을 사용하여 병렬처리가 가능한지 &lt;/span&gt;대략은 이해가 되었으면 좋겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;js시리즈 다음글은 테스크 큐에서의 우선순위에 대한 글이 될 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>javascript</category>
      <category>browser</category>
      <category>call stack</category>
      <category>javascript</category>
      <category>JS 런타임</category>
      <category>nodejs</category>
      <category>병렬처리</category>
      <category>비동기</category>
      <category>싱글스레드</category>
      <category>이벤트루프</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/174</guid>
      <comments>https://coding-lks.tistory.com/174#entry174comment</comments>
      <pubDate>Thu, 2 Jun 2022 23:29:32 +0900</pubDate>
    </item>
    <item>
      <title>OOP(객체 지향 프로그래밍)이란? with typescript</title>
      <link>https://coding-lks.tistory.com/173</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;OOP(객체 지향 프로그래밍)이란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;필요한 데이터를 추상화&lt;/span&gt;시켜 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;상태와 행위를 가진 객체&lt;/span&gt;를 만들고 그 객체들 간의 유기적인 상호작용을 통해 로직을 구성하는 컴퓨터 프로그래밍 패러다임 중 하나입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 지향 프로그래밍은 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;응집력이 높고 결합력이 낮다는 특징&lt;/span&gt;이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;응집력이 높다?&lt;br /&gt;프로그램의 한 요소가 특정 목적을 위해 밀접하게 연관된 기능들이 모여서 구현 되어 있고, &lt;br /&gt;지나치게 많은 일을 하지 않으면 그것을 응집력이 높다고 표현한다.&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;결합력이 낮다?&lt;br /&gt;- 다른 요소들과&amp;nbsp;관계를 크게 맺고 있지 않고 덜 의존적이라는 것을 의미한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래에서는 객체지향프로그래밍의 특성 중 가장 대표적인 추상화, 캡슐화, 상속성, 다형성에 대한 개념을 typescript 예시 코드와 함께 다뤄보았습니다 :)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;추상화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 객체지향 관점에서 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;클래스(상태와 행위를 가진 객체)&lt;/span&gt;를 정의하는 것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;여기서 상태는 필드, 행위는 메서드라고 생각하시면 이해하기 편합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하자면 공통의 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;속성이나 기능을 묶어 이름을 붙이는 것!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;특징&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;불필요한 부분(구현부)을 숨긴다. &lt;br /&gt;- 추상화된 클래스를 사용만하는 입장에서는 어떤 동작을 하는지만 파악이 되면 구현부에 대한 구체적인 이해없이 사용하는데 지장이 없음&lt;/li&gt;
&lt;li&gt;인터페이스(기능명세)와 구현을 분리한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 구현부 없이 인터페이스만 정의하여 추상화한 코드입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1653829413024&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface PersonInterface {
	age: string
	name: string

	updatePassword(): Promise&amp;lt;string&amp;gt;
	getAge(): number
	setAge(age: number): void
	getName(): string
	setName(name: string): void
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;캡슐화 - 데이터 보호&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 캡슐화 : 필드와 메서드를 하나(객체)로 묶는 것 입니다.&lt;/li&gt;
&lt;li&gt;은닉화 : 객체의 세부 내용이 외부에 드러나지 않아 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;외부에서 데이터를 직접 접근하는 것을 방지&lt;/span&gt;한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;외부에 영향 없이 객체 내부 구현 변경 가능&lt;/span&gt; - Person의 setAge기능이 바뀐다면 해당 메서드에서의 수정만을 통해 변경의 전파를 최소화할 수 있습니다!&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1653829502381&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Person {
    private age: number;
    private name: string;

    public getAge(): number {
        return this.age;
    }
    public setAge(age: number): void{
        this.age = age;
    }
    public getName(): string {
        return this.name;
    }
    public setName(name: string): void {
        this.name = name;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Person 이라는 클래스에 이름, 나이의 멤버변수(상태값)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 클래스의 이름, 나이는 접근 제한자가 private 이기 때문에 인스턴스에서 직접 접근해 수정할 수 없으며 선언된 get , set 메서드로만 수정이 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;상속&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자식클래스가 부모클래스의 특징과 기능을 물려받았기 때문에 중복 코드를 줄일 수 있다.&lt;/li&gt;
&lt;li&gt;클래스에 메소드 추가가 어려운 경우에 사용합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다른사람이 작성한 클래스를 가져와 사용하는 경우 (해당 객체에 대한 이해가 완벽하지 않은 경우)&lt;/li&gt;
&lt;li&gt;클래스가 다양한 곳에서 상속 받아 쓰이는 경우 (파악하지 못한 곳에서는 추가한 메서드가 불필요한 기능일 수 있음)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1653829565301&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Animal {
  private name: string;

  public setName(name: string): void {
    this.name = name;
  }
  public getName(): string {
    return this.name;
  }
  public cry(): void {
    console.log('울음소리!');
  }
}

class Cat extends Animal {
  cry(): void {
    console.log('야옹야옹');
  }
}

const cat = new Cat();
cat.setName('나비');
console.log(cat.getName()); // 나비 - 부모 클래스에서 물려받음 setName, getName메서드가 정상적으로 동작함을 보장

cat.cry(); // 야옹야옹 - 재정의한 울음소리가 콘솔로그에 출력됩니다!&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cat 클래스는 Animal 인터페이스를 상속받았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상속시에 모든 필드, 메서드를 물려 받았기 때문에 setName, getName메서드가 정상적으로 작동합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 중복코드를 줄이고 수정하고 싶은 cry 메서드만 오버라이딩(재정의)하여 Cat 클래스를 정의했음을 볼 수 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;다형성 - 객체 변경 용이&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;다형성을 가능하게 하는 것들&amp;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;오버라이딩(overriding)&lt;/b&gt;&amp;nbsp;: 부모클래스 메서드를 자식클래스에서 재정의하는 것&lt;/p&gt;
&lt;pre id=&quot;code_1653829644960&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Animal {
  public cry(): void {
    console.log('울음소리!');
  }
}

class Cat extends Animal {
  cry(): void {
    console.log('야옹야옹');
  }
}

const animal = new Animal();
animal.cry(); //부모 클래스의 cry() 메소드입니다.

const cat = new Cat();
cat.cry(); //자식 클래스의 cry() 메소드입니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;오버로딩(overloading)&lt;/b&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;method의 이름은 같지만 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;매개변수의 개수는 동일&lt;/span&gt;하게 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;type은 다르게 정의&lt;/span&gt;하여 사용하는 방법입니다&lt;/li&gt;
&lt;li&gt;다른 언어의 Overloading 개념은 method명만 같으면 되지만 typescript는 Overloading을 사용하기 위해서는 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;함수명과 매개변수의 개수가 같아야 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1653829716529&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Example {
  // 함수의 선언
  shot(input: number): void;
  shot(input: number[]): void;

  // 함수의 구현
  // 유니온 타입으로 선언 &amp;amp; 타입 가드
  shot(input: number | number[]): void {
    if (typeof input === 'number') console.log(`input : number type`);
    else console.log('input : number[] type');
  }
}

const example = new Example();
example.shot(3);
example.shot([1, 2, 3]);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 사이드프로젝트에 OOP를 적용하기전의 준비과정으로 OOP를 적용하기 위해 필수적인 개념들을 typescript 코드와 함께 간단하게 알아보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오버로딩 관련 문법은 저도 많이 사용하지 않아 어색한 문법이라 의미가 있는 포스팅이었습니다.&lt;br /&gt;이를 계기로 typescript도 따로 시간을 내서 한번 다룰 필요성을 느낄 수 있었습니다 :)&lt;br /&gt;&lt;br /&gt;오늘 포스팅은 이만 마무리 하겠습니다~!&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;참조 :&amp;nbsp;&lt;br /&gt;&lt;a href=&quot;https://lakelouise.tistory.com/194&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://lakelouise.tistory.com/194&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://doitnow-man.tistory.com/entry/typescript-11-%EC%98%A4%EB%B2%84%EB%9D%BC%EC%9D%B4%EB%94%A9%EA%B3%BC-%EC%98%A4%EB%B2%84%EB%A1%9C%EB%94%A9-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%B6%94%EC%83%81-class&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://doitnow-man.tistory.com/entry/typescript-11-%EC%98%A4%EB%B2%84%EB%9D%BC%EC%9D%B4%EB%94%A9%EA%B3%BC-%EC%98%A4%EB%B2%84%EB%A1%9C%EB%94%A9-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%B6%94%EC%83%81-class&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@0sunset0/OOP%EC%9D%98-%EB%84%A4%EA%B0%80%EC%A7%80-%ED%8A%B9%EC%A7%95%EC%B6%94%EC%83%81%ED%99%94%EC%BA%A1%EC%8A%90%ED%99%94%EC%83%81%EC%86%8D%EB%8B%A4%ED%98%95%EC%84%B1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://velog.io/@0sunset0/OOP%EC%9D%98-%EB%84%A4%EA%B0%80%EC%A7%80-%ED%8A%B9%EC%A7%95%EC%B6%94%EC%83%81%ED%99%94%EC%BA%A1%EC%8A%90%ED%99%94%EC%83%81%EC%86%8D%EB%8B%A4%ED%98%95%EC%84%B1&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://simsimjae.tistory.com/293&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://simsimjae.tistory.com/293&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>etc</category>
      <category>OOP</category>
      <category>typescript</category>
      <category>객체지향프로그래밍</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/173</guid>
      <comments>https://coding-lks.tistory.com/173#entry173comment</comments>
      <pubDate>Sun, 29 May 2022 22:18:25 +0900</pubDate>
    </item>
    <item>
      <title>글또 7기 다짐글</title>
      <link>https://coding-lks.tistory.com/172</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;시작하기 전&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 회사업무,&amp;nbsp;DND,&amp;nbsp;리팩터링&amp;nbsp;스터디&amp;nbsp;등 많은 활동들을 꾸준히 했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 많은 활동을 하면서 노션에 기록을 남기긴 했지만 블로그에 포스팅할 정도로 깊게 정리되진 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 최근 DND에서 진행한 프로젝트가 1차 배포되며 조금은 여유로워지기도 했고 블로그에 그동안 노션에서 정리했던 내용, 내가 부족하다고 느낀 점들을 한 번 더 정리하는 시간을 가지기로 마음을 먹었다.&lt;br /&gt;그러던 중 회사 동료의 소개로 글또를 알게 되었고 내가 포기하지 않고 꾸준히 글을 작성하는 데 직간접적으로 도움을 받을 수 있겠다는 생각에 바로 지원하게 되었다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;366&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lqTxE/btrB68otu48/jhaXWq45XuIQf6dAk37BmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lqTxE/btrB68otu48/jhaXWq45XuIQf6dAk37BmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lqTxE/btrB68otu48/jhaXWq45XuIQf6dAk37BmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlqTxE%2FbtrB68otu48%2FjhaXWq45XuIQf6dAk37BmK%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;590&quot; height=&quot;366&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;366&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;못먹어도 GO!&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;글또에서 얻고 싶은 것 and 다짐&lt;/b&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;그동안 고이 모셔 놓았던 학습 내용들을 다시 한번 정리하며 톺아보기&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;동아리 활동을 하면서 익혔던 typeorm, ts 등등 특히 기본이 되는 프레임워크, orm, 언어에 대해서도 다시 한번 정리하는 시간을 가져야겠다는 필요성을 개발을 하면서 계속해서 느낀다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;한번 쓰고 말았던 기술들, 지식에(ex 리팩토링, 퍼페티어, elk) 대한 지식을 고도화시키기!&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;회사 업무에서 사용하며 처음 접했던 기술들 puppeteer(headless browser), elk 등등 업무가 끝난 후에 제대로 정리해야지 하면서 미뤄두었던 기술 부채들을 해결하기 위해서!&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;내게 부족한 소프트웨어 설계 능력 기르기!&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;현재 가장 부족하다고 느끼는 능력이기 때문에 아마 초반 대부분은 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;소프트웨어 설계, 디자인 패턴&lt;/span&gt;에 관련된 내용을 중심으로 블로그 포스팅을 이어 나갈 예정이다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;앞서 작성한 블로그 포스팅들에는 내 생각이 거의 들어가 있지 않다.&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;글또를 하며 작성한 글에는 내 생각을 최대한 많이 담는 게 목적이다.&lt;br /&gt;이제는 학습한 내용을 정리하는 데에서 끝나지 않고 내 고민, 생각들을 최대한 많이 녹아든 글을 작성고자 하는 목표가 생겼다 :)&lt;br /&gt;이전에 작성했던 글들을 보면서 다짐하게 되었다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;다루게될 글감&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;객체지향적 설계(oop)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;tdd&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;프로젝트 nest로 컨버팅 + oop적용 + tdd적용&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;확장을 고려한 DB설계&lt;/li&gt;
&lt;li&gt;리팩터링&lt;/li&gt;
&lt;li&gt;퍼페티어 or elk&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;써놓고 보니 해야하고 하고싶은게 엄청 많아 보인다 ㅎ.ㅎ&lt;br /&gt;하나 더 바라는건 글또 활동을 함께 하게된 동료분들과도 많은 교류를 할 수 있기를 바란다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 6개월 안에 다 다룬다기 보다는&lt;span style=&quot;color: #000000; background-color: #9feec3;&quot;&gt; 6개월 후에 글또 회고글을 쓸 수 있기를&lt;span style=&quot;background-color: #ffffff;&quot;&gt; 바라는 짤과 함께&amp;nbsp;&lt;/span&gt;&lt;/span&gt;글을 마친다 :)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;420&quot; data-origin-height=&quot;307&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d0ADPh/btrB7cQICWX/xoc6UdpavRxrCIKKZkr5lK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d0ADPh/btrB7cQICWX/xoc6UdpavRxrCIKKZkr5lK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d0ADPh/btrB7cQICWX/xoc6UdpavRxrCIKKZkr5lK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd0ADPh%2FbtrB7cQICWX%2Fxoc6UdpavRxrCIKKZkr5lK%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;420&quot; height=&quot;307&quot; data-origin-width=&quot;420&quot; data-origin-height=&quot;307&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;회고까지 가즈아~&lt;/p&gt;</description>
      <category>Life</category>
      <category>글또</category>
      <category>다짐글</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/172</guid>
      <comments>https://coding-lks.tistory.com/172#entry172comment</comments>
      <pubDate>Sun, 15 May 2022 20:08:00 +0900</pubDate>
    </item>
    <item>
      <title>DND 6기 후기</title>
      <link>https://coding-lks.tistory.com/171</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;DND가 뭔데?&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-03-06 오후 8.27.55.png&quot; data-origin-width=&quot;2142&quot; data-origin-height=&quot;980&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbBnx3/btrveLTsGpp/E6Xrjx0AQ688oy8wK1ngS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbBnx3/btrveLTsGpp/E6Xrjx0AQ688oy8wK1ngS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbBnx3/btrveLTsGpp/E6Xrjx0AQ688oy8wK1ngS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbBnx3%2FbtrveLTsGpp%2FE6Xrjx0AQ688oy8wK1ngS1%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;666&quot; height=&quot;305&quot; data-filename=&quot;스크린샷 2022-03-06 오후 8.27.55.png&quot; data-origin-width=&quot;2142&quot; data-origin-height=&quot;980&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자 4명 디자이너 2명이 한팀을 이루어 8주간 프로젝트를 구현 런칭까지 한 사이클을 도는 it연합 동아리 중 하나이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어쩌다 취업을 하게됬지만 it연합동아리는 꼭 한번 참가해보고 싶었고 회사밖에서의 인적네트워크를 형성하고 싶었고 여건이 된다면 마침 공부하던 nest.js 프레임워크를 사용해 프로젝트를 진행하고싶어 지원하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 DND 덕분에 2022년은 갓생모드로 출발 할 수 있었다 :)&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;사실 전기수인 5기 모집때 지원했었는데 떨어졌던 경험이 있다 &lt;span&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;APPLY&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;지원&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신청 구글폼이 열리자마자 초안을 작성하고 시간 날때마다 수정했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음엔 이정도면 이번에는 되겠지?라고 생각했었는데 신청 마감일에 60명 모집에 무려 1200명이 지원한걸 확인할 수 있었고 기대를 접어두었다 ㅋ.ㅋ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;팀빌딩&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여차저차 기대를 접어두었던 동아리 합격을 하고 활동을 시작하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀빌딩은 운영진분들께서 기술 스택, MBTI 등 여러가지 정보를 고려하여 진행해주셨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MBTI도 고려해 팀빌딩을 한다는게 재밌는 포인트였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀빌딩이 된 후 팀원분들의 github를 구경했는데 개발파트 분들은 나를 제외하고 학생 or 취준생분들로 구성되었다는걸 먼저 알고 구경했는데 내가 commit수도 내가 제일 적었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다들 1200명을 뚫고 오신분들이라 그런지 대외활동, 프로젝트 경험이 있으셔서 DND는 순탄하게 진행되겠구나 생각을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;DND 활동 요약&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 사진은 DND에서의 8주간의 가이드입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무조건적으로 따를 필요는 없이 해당 가이드라인을 토대로 필요한 부분을 추가해가며 활동하기를 추천드립니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-03-06 오후 8.28.14.png&quot; data-origin-width=&quot;1886&quot; data-origin-height=&quot;1610&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAJ8Wh/btru3MsuYLn/Q8tVNmKII61ePVcQgyOmhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAJ8Wh/btru3MsuYLn/Q8tVNmKII61ePVcQgyOmhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAJ8Wh/btru3MsuYLn/Q8tVNmKII61ePVcQgyOmhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAJ8Wh%2Fbtru3MsuYLn%2FQ8tVNmKII61ePVcQgyOmhk%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;637&quot; height=&quot;544&quot; data-filename=&quot;스크린샷 2022-03-06 오후 8.28.14.png&quot; data-origin-width=&quot;1886&quot; data-origin-height=&quot;1610&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 스케쥴처럼 딱딱 일이 진행된다면 정말 좋겠지만 세상엔 예상치 못한 일들이 너무 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 8주라는 기간에 주니어 or 대학생(취준생) 개발자가 하나의 프로젝트를 완성하려면 프로젝트의 규모를 줄이던가 개발을 빨리 시작해야한다는 걸 인지하고 있었기 때문에 3~4주차의 스케쥴을 1~2주차 때 함께 시작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 7기에 참가하게 된다면 개발은 빨리 들어가면 빨리 들어갈수록 좋다는 말을 해주고 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기획&amp;amp; 디자인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 회의 일정을 내가 조율하면서 자연스럽게 팀장 포지션을 맡게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 우리팀은 &amp;lt;&lt;b&gt;사회초년생을 위한 가이드, Spoon Feed&lt;/b&gt;&amp;gt; 서비스를 기획했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 아이디어가 여러가지 나왔는데, 우리에게 가장 필요하다고 느꼈고 8주라는 일정을 고려하여 선정된 아이디어이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 기획부분이 여러가지 개인사정으로 딜레이 되었었는데 2주차 때 솔직히 프로젝트 진행속도가 굉장히 느려져서 막막했던 기억이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다행히 팀원분들 모두 딜레이 된만큼 열일해주셔서 mvp단계에서의 서비스 규모와 기능에 대한 명세를 할 수 있었다.(특히 다자인 파트 분들이 서비스를 정의하는데 고생을 해주셨다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 디자이너의 역할에 대해 제대로 알게 된 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어플리케이션에 대한 디자인 뿐만 아니라 해당 서비스의 방향성을 잡아주는 역할을 해주는? 능력있는 분들이 같은팀이라 행운이었다.(기획 or 유저의 사용성 등등)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전에 사이드 프로젝트를 하며 나도 페르소나나 IA작성에 참여 해본적이 있었는데 디자이너분들의 디테일은...  &amp;nbsp;x 30000&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기능 구현&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 준비가 모두 끝나고 기능 구현에 들어갈 차례이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백엔드에서 api를 개발하면 안드로이드 분들께 스웨거 + 노션으로 전달드렸고 다음 사진들에서 우리 문서의 일부 모습을 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-03-06 오후 8.29.55.png&quot; data-origin-width=&quot;2140&quot; data-origin-height=&quot;932&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c75UOW/btru9qhCLdq/JSGgBVIbTA6kASNC6fFri0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c75UOW/btru9qhCLdq/JSGgBVIbTA6kASNC6fFri0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c75UOW/btru9qhCLdq/JSGgBVIbTA6kASNC6fFri0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc75UOW%2Fbtru9qhCLdq%2FJSGgBVIbTA6kASNC6fFri0%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;603&quot; height=&quot;263&quot; data-filename=&quot;스크린샷 2022-03-06 오후 8.29.55.png&quot; data-origin-width=&quot;2140&quot; data-origin-height=&quot;932&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-03-06 오후 8.30.35.png&quot; data-origin-width=&quot;1966&quot; data-origin-height=&quot;788&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ra2uo/btru7RfJ5hQ/RyUds4QJzCwPX3qSNjuDI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ra2uo/btru7RfJ5hQ/RyUds4QJzCwPX3qSNjuDI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ra2uo/btru7RfJ5hQ/RyUds4QJzCwPX3qSNjuDI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRa2uo%2Fbtru7RfJ5hQ%2FRyUds4QJzCwPX3qSNjuDI1%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;594&quot; height=&quot;238&quot; data-filename=&quot;스크린샷 2022-03-06 오후 8.30.35.png&quot; data-origin-width=&quot;1966&quot; data-origin-height=&quot;788&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 규모를 줄인다고 줄였는데도 직장과 병행하려니 새벽까지 작업을 해야했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발에 들어가니 재미는 있었는데 DND 스케쥴 상 잠을 줄여야만 했다ㅜ.ㅜ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;좋았던 점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 클라우드 비용 지원 등등 여러가지 금전적인 지원, 세미나도 너무 좋았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 내가 팀운이 좋은건지 모르겠지만 열정있고 능력있는 팀원분들과 함께 작업을 할 수 있는게 제일 좋았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디자인과 안드로이드 분들도 회의 때도 그렇고 수시로 ui, 디자인 관련해 소통을 해주시는데 매번 열정을 느낄 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀장으로서 여러가지로 부족한점이 많았는데 다들 불평/불만 없이 개인의 능력으로 매꿔주신 느낌이라 팀원분들 모두에게 감사할 따름이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백엔드는 같이 참여하게된 팀원분이 너무 잘해주셔서 놀랐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 대학생이신데도 늘어질 틈 없이 작업물로 자극을 주시니 나도 원래 생각했던것 보다 훨씬 열심히 참여할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 기술스택이 내가 주로 다뤘던 스택으로 정해지게 되면서 TDD적용하기를 미뤄뒀었는데 js나 sql은 잘 모르신다고 하셨었는데 키워드만 알려드려도 금방 작업을 해버리셔서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lsquo;아 기술적으로 더 욕심을 내도 문제가 없겠는데?&amp;rsquo;라는 생각을 하게 됬었고 지금은 확신이 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 아직 말씀은 안드렸는데 DND과정 이후의 추가 개발 과정에서는 TDD, 리팩터링, nest로의 전환 등등 여러가지를 적용해도 무리가 없겠다는 생각을 하게 되었다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;피드백&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 우선 팀장 포지션을 맡으면서 느낀건 회의를 진행할 때 회의 주제에 대한 문서를 최대한 자세하게 작성 후 하루전에는 공유를 해야겠다는 생각을 했다. &amp;rArr; 회의 주제에 대해 미리 공유가 되지않아 회의 시간이 불필요하게 길어지는 상황이 있었음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 화상회의는 녹화기능이 있는 플랫폼으로!(비대면)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DND에서는 디스코드를 사용해 화상회의를 진행하는걸 권유한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 디스코드에서 화상 녹화 기능을 지원하지 않아 회의를 할 때 타이핑을 하여 모&amp;nbsp; 든&amp;nbsp; 사항을 기록해야 했고 제한사항이 많았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간에 &amp;lsquo;서기 역할을 그냥 내가 담당해서 할까?&amp;rsquo; 생각도 했지만 그러면 회의에 집중도가 떨어지기 때문에 화상회의는 녹화기능이 있는 플랫폼을 사용하는게 가장 좋겠다는 생각을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. slack&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 현업에서 업무툴로 사용하는 슬랙의 사용을 주장하고 싶었지만(DND에서의 권장사항이기도 하다.) 초기에 슬랙이 무료버젼(일정 분량이 차면 오래된 글 순으로 삭제됨)이라 강력하게 말을 할수가 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DND 활동 막바지에는 PRO버젼으로 업그레이드 해주셨지만 7기 때는 처음부터 PRO버전으로 사용했으면 좋겠다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리팀은 대부분의 소통을 카톡, 노션을 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주니어 개발자로서 일하고 있는 내 입장에서도 DND에 참여하면서 얻은 점이 되게 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀을 리딩하는 경험, 프로젝트를 기획부터 배포까지 할 수 있는 경험, 열정있고 능력있는 분들과 함께 프로젝트를 할 수 있는 경험 등등&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 회사 업무가 너무 바빠서 제대로 참여를 할 수 있는 상황이 아니라면 고민을 해봐야겠지만 그게 아니라면 지원하지 않을 이유가 없는 동아리이다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 취준생, 학생분들이라면 얻는게 압도적으로 많을 것이니 DND지원을 무조건 추천한다  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1646566762521&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;DND&quot; data-og-description=&quot;프로젝트에 즐거움을, 모두에게 기회를&quot; data-og-host=&quot;dnd.ac&quot; data-og-source-url=&quot;https://dnd.ac/&quot; data-og-url=&quot;https://dnd.ac/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/zCe87/hyNC5ZCvZj/jgaGRtCOK87F2mhZN9BkAK/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400&quot;&gt;&lt;a href=&quot;https://dnd.ac/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dnd.ac/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/zCe87/hyNC5ZCvZj/jgaGRtCOK87F2mhZN9BkAK/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400');&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;DND&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;프로젝트에 즐거움을, 모두에게 기회를&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dnd.ac&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>Life</category>
      <category>DND</category>
      <category>DND 6기</category>
      <category>DND 리뷰</category>
      <category>DND 후기</category>
      <category>IT동아리</category>
      <category>it연합 동아리</category>
      <category>Spoon feed</category>
      <category>사이드 프로젝트</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/171</guid>
      <comments>https://coding-lks.tistory.com/171#entry171comment</comments>
      <pubDate>Sun, 6 Mar 2022 20:36:54 +0900</pubDate>
    </item>
    <item>
      <title>2021년 회고</title>
      <link>https://coding-lks.tistory.com/170</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;1월&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;42서울 과정을 진행하며 cub3d와 씨름하던 시절&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데스크탑도 새로 장만하고~&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멘토링받고 멘탈도 탈탈 털리고~ 내가 왜 개발자가 되고 싶은건지 다시 한번 돌아보는 시간을 가지게 되었다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백엔드쪽에 관심을 가지게 된 시점!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당시 가장 자신있던 언어인 python을 사용하여 Django웹 프레임워크 공식문서도 보고 클론코딩도 하고 개인프로젝트도 시작했던 때(이 때 공식문서와 좀 친해지는 시간을 많이 가졌었다...)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코로나로 클러스터가 닫혀 본가에 피신해있을때!!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2월&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하고 싶었던 Django기반의 개인프로젝트도 배포해보고(지금 보면 엉망진창이지만 ㅋ.ㅋ)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬이란 언어에 대해서 조금이나마 깊이 공부했을 때&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금은 가물가물하지만~&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정말 매일매일 공부했지만 너무 모르는게 많고 알아야할게 산더미라는걸 많이 느꼈던 시점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 첫 프로젝트를 진행한 경험이 너무 소중했다 ~~&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3월&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코로나가 잠잠해져 클러스터를 다시 나가기 시작했다!!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 공부를 하느라 잠시 손을 놨던 42seoul 과제를 다시 하기 시작했습니당~&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 새로운 프로젝트에 참여하게 되었습니다~&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 참여하게 된 이유는 2가지가 있는데&lt;/p&gt;
&lt;ol 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;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 오프라인 회의때가 아직 기억나는데 모두 말도 잘하고 열정 있는 분들이라는 생각을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 한번도 안 써본 javascript, express를 기술스택으로 쓴다는게 걸리긴 했지만 이미 오랜 삽질의 경험으로 새로운 것에 대한 두려움은 없던터라 무지성 참여~(이 땐 몰랐다 회의만 1달 반을 할줄은...)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아 그리고 스코페 2021에도 참여하여 알고리즘 문제를 풀었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알고리즘 공부를 놓은지 꽤 시간이 지나서 광탈의 충격으로 알고리즘 문제풀이를 다시 시작했다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때부터 1년이 바빠지기 시작해 블로그 활동을 못했었다.&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;4월&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;42seoul 과제와 Matching42 기획 회의, 팀원분들과의 JS스터디, 알고리즘 공부로 바쁘게 지나간 한달이다...!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JS... 처음하기도 하고 팀원분들이 굉장히 DEEP하게 스터디를 진행해서 버겁기도 했지만 스터디장님?께서 너무 잘 이끌어 주셔서 엄청 많이 배웠다.&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;5월&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본격적으로 Matching42 개발을 시작했고, JS스터디, 면접 CS스터디, CS스터디, 알고리즘 스터디 등등 참여하는게 너무 많아서 쉴 새 없이 개발, 공부만 했던 달이다.&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;Matching42개발을 처음 시작했을 때 npm install도 package.json도 몰라서 &amp;lsquo;음... 개발환경은 각자 구성하는 건가&amp;rsquo; 어떻게 하는거지 하며 2일동안 끙끙 대기도 했던 기억이 있다.ㅋ.ㅋ(결국 스터디하면서 팀원분들에게 여쭤보고 민망했던 기억이...)&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;6월&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5월과 마찬가지로 폭.풍 개발, 공부로 한달이 정말 금방 지나갔다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 IT연합동아리(넥스터즈, DND)에 지원했지만 포폴이 없던 때라 그런지 경쟁률이 치열해서인지 광탈하게 되어서 포트폴리오를 만들어야 겠다는 생각을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JS스터디, 면접 CS스터디도 6월과 함께 마무리 되었다.&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;7월&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Matching42에서 어느정도 코딩단계가 거의 끝나가 2주간의 휴식기간을 가지기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 한창 포트폴리오를 만들고 있었는데 마침 42seoul에서 포트폴리오 작성 특강을 한다고 해서 듣고 포트폴리오를 만들게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 특강을 해주신 분이 텔런트엑스라는 개발자 채용 플랫폼의 CEO이신데 포트폴리오를 텔런트엑스(코테를 보는 신기한 플랫폼)에 등록하면 잘 작성한 5명에게 배민 쿠폰을 준다고 해서 바로 등록해서 배민 쿠폰 Get&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7월은 한가할줄 알았지만 올려둔 포트폴리오를 통해 한 스타트업과 면접을 매칭시켜줘서 면접준비를 한다고 갑자기 바빠졌다.&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;8월&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매칭된 회사와 면접을 보게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 이 회사에 대해서는 몰랐지만 기회를 준것이라 생각해서 면접준비 열심히 하고 면접을 봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;면접을 볼 당시에 사실 생각보다 깊은 질문을 안하셔서 나한테 관심이 없는건가...?라는 생각을 했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쨋든 지금 다니게 된 회사에 9월에 입사를 하게 되었고 출근 전 까지는 맘껏 놀기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제주도로 힐링여행도 가고 바다보러 놀러가려 했지만 코.로.나로 인해 모든 계획이 무산되어 본가에서 친구들과 시간 보내고 Matching42서버를 AWS를 통해 배포를 한다고 개고생하며 꾸역꾸역 배포도 하고 CI/CD도 구성했다.(CS스터디에서 했던 네트워크 공부, ft_server과제를 했던게 도움이 많이 되었다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;9월&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신입사원 이그래&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스스로 멘탈이 강한편이라고 생각했는데 9월은 꽤나 힘들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 9월은 연휴가 많았고 같은날 입사하신 경력자분이 신경 써주셔서 그나마 다행이었다.&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;10월&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;11월&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서 업무를 하며 코딩 부분에서 부족함을 느껴서 리팩터링 스터디를 참여하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;솔직히 취업하기 전에는 회사다니면서 공부하는거 별 거 아닌 줄 알았는데 진짜진짜진짜 대단하다는걸 몸으로 겪으며 느낀다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;12월&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쓰고보니 개발관련된 이야기가 거의 대부분이라니 ~.~&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2020년도에도 열심히는 했었지만 뭔가 답답한 마음이 있었는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2021년도에는 방향을 잡고 더 똑똑하게 공부하고 준비를 했다는 생각이 든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1년을 되돌아보니 참 일을 많이 벌려놨고 그만큼 힘들지만 얻어 간게 많은 년도라는 생각이 든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 정했던 목표들도 하나씩 도달하고 있다는게 느껴져서 Good&lt;/p&gt;</description>
      <category>Life</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/170</guid>
      <comments>https://coding-lks.tistory.com/170#entry170comment</comments>
      <pubDate>Tue, 21 Dec 2021 08:02:48 +0900</pubDate>
    </item>
    <item>
      <title>01_리팩터링-첫 번째 예시</title>
      <link>https://coding-lks.tistory.com/169</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;리팩터링 2판을 보며 정리한 내용입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.2 예시 프로그램을 본 소감&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;나는 수백줄짜리 코드를 수정할 때면 먼저 &lt;b&gt;프로그램의 작동 방식&lt;/b&gt;을 더 쉽게 파악할 수 있도록 코드를 여러함수와 프로그램 요소로 재구성한다.&lt;/li&gt;
&lt;li&gt;구조가 빈약하다면 구조부터 바로잡은 뒤에 기능을 수정하는 편이 작업하기가 훨씬 수월하다.(선: 리팩토링, 후: 기능추가)&lt;/li&gt;
&lt;li&gt;잘 작동하고 나중에 변경할 일이 절대 없다면 코드를 현재 상태로 놔둬도 아무런 문제가 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.3 리팩터링이 첫 단계&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;먼저 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;테스트 코드&lt;/b&gt;&lt;/span&gt;부터 마련해야한다.&lt;/li&gt;
&lt;li&gt;테스트를 작성하는 데 시간이 좀 걸리지만, 신경 써서 만들어두면 디버깅 시간이 줄어서 전체 작업 시간은 오히려 단축된다. (&amp;rarr; 4장)(아무리 간단한 수정이라도 리팩터링 후에는 항상 테스트하는 습관!)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.4 statement()함수 쪼개기&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;i&gt;반복문 쪼개기&lt;/i&gt; &amp;rarr; 변수값을 누적시키는 부분을 분리합니다.&lt;/li&gt;
&lt;li&gt;문장 슬라이드 &amp;rarr; 변수 초기화 문장을 변수값 누적 코드 바로앞으로 옮긴다.&lt;/li&gt;
&lt;li&gt;함수 추출하기 &amp;rarr; 적립 포인트 계산 부분을 별도 함수로 추출한다.&lt;/li&gt;
&lt;li&gt;&lt;i&gt;변수 인라인 하기&lt;/i&gt; &amp;rarr; 로컬 변수(임시변수) 제가하기&lt;/li&gt;
&lt;li&gt;전체 동작을 각각의 부분으로 나눌 수 있는 지점을 찾는다. &amp;rarr; switch&lt;/li&gt;
&lt;li&gt;(함수 반환값 - result라는 이름 사용)&lt;/li&gt;
&lt;li&gt;컴퓨터가 이해하는 코드는 바보도 작성할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사람이 이해하도록 작성하는 프로그래머&lt;/b&gt;가 진정한 실력자다.&lt;/li&gt;
&lt;li&gt;좋은 코드라면 하는 일이 명확히 드러나야 함 &amp;rarr; &lt;b&gt;변수 이름&lt;/b&gt; 중요!&lt;/li&gt;
&lt;li&gt;로컬변수 제거 &amp;rarr; 추출 작업이 훨씬 쉬워진다는 장점&lt;/li&gt;
&lt;li&gt;반복문이 쪼개서 성능 느려진다? 미미함&lt;/li&gt;
&lt;li&gt;만약 성능이 느려진다면, 리팩토링 후 성능개선하는 것이 효과적따라서 특별한 경우가 아니면 무시하라&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.7 중간 점검: 두 파일(과 두 단계)로 분리됨&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리팩토링후 44줄 짜리 코드 &amp;rarr; 70줄 (함수로 추출하면서 함수 본문 열고 닫는 괄호가 덭붙여져서 그렇다)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;추가된 코드 덕분에 전체 로직을 구성하는 요소 각각이 뚜렷이 부각되고, 계산하는 부분과 출력형식을 다루는 부분이 분리됐다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;이렇게 모듈화하면 각 부분이 하는 일과 그 부분이 맞물려 돌아가는 과정을 파악하기 쉬워진다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;간결함이 지혜의 정수일지 몰라도, 프로그래밍에서만큼은 &lt;b&gt;명료함&lt;/b&gt;이 진화할 수 있는 소프트웨어 정수다.&lt;/li&gt;
&lt;li&gt;항시 코드베이스를 작업 시작 전보다 건강하게(healthy) 만들어놓고 떠나야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.8 다형성을 활용해 계산 코드 재구성하기&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;조건부로직을 다형성으로 바꾸기
&lt;aside&gt;  다형성(polymorphism)이란 하나의 객체가 여러 가지 타입을 가질 수 있는 것을 의미합&lt;/aside&gt;
&lt;/li&gt;
&lt;li&gt;&amp;rarr; 코드 수정시에 조건부로직을 각각 수정하도록 코드를 작성하는 것보다는 서브 클래스를 추가하는 것이 확장성, 가독성 면에서 좋다.&lt;/li&gt;
&lt;li&gt;생성자를 팩터리 함수로 바꾸기!&lt;/li&gt;
&lt;li&gt;&amp;rarr; &lt;b&gt;조건문은 오직 팩터리 함수에서만&lt;/b&gt; 사용하도록 한다!&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;기억에 남는 부분&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;경험 많은 프로그래머조차 코드의 실제 성능을 정확히 예측하지 못한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;소프트웨어 성능은 대체로 코드의 몇몇 작은 부분에 의해 결정되므로 그 외의 부분은 수정한다고 해도 성능 차이를 체감할 수 없다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;잘 다듬어진 코드라야 성능 개선 작업도 훨씬 수월하기 때문이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;좋은 코드를 가늠하는 확실한 방법은 '얼마나 수정하기 쉬운가'이다&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;정리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 42seoul을 진행하며 간단하게 shell script로 test코드를 작성해 본 적은 있지만 최근에야 회사 업무를 하며 pytest와 같은 테스트 프레임워크를 사용해보고 테스트가 왜 중요한지 느끼는 와중에 1.3의 내용을 보니 확 와닿았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 코드를 최적의 코드로 짜는게 제일 좋겠지만 사실 프로그램의 성능에는 작은 부분(핵심 로직)에 의해 결정되고 이를 파악하려먼 구조가 잘 잡혀있어야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팩토리 함수라는 개념을 몰랐었는데 정리를 한번 해보기로!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&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;관련 코드는 github저장소에서 확인가능합니다.&lt;/p&gt;
&lt;figure id=&quot;og_1639954558369&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;GitHub - Metacognition-Polymath/refactoring-2nd-edition: 코드 구조를 체계적으로 개선하여 효율적인 리팩터링&quot; data-og-description=&quot;코드 구조를 체계적으로 개선하여 효율적인 리팩터링 구현하기. Contribute to Metacognition-Polymath/refactoring-2nd-edition development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/Metacognition-Polymath/refactoring-2nd-edition/tree/master/01_%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81-%EC%B2%AB-%EB%B2%88%EC%A7%B8-%EC%98%88%EC%8B%9C/01_%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81_%EC%B2%AB%EB%B2%88%EC%A7%B8-%EC%98%88%EC%8B%9C_kiwi&quot; data-og-url=&quot;https://github.com/Metacognition-Polymath/refactoring-2nd-edition&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bT4KEQ/hyMKECabwK/0JsMsVUVzWj5KJMt572ph0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/Metacognition-Polymath/refactoring-2nd-edition/tree/master/01_%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81-%EC%B2%AB-%EB%B2%88%EC%A7%B8-%EC%98%88%EC%8B%9C/01_%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81_%EC%B2%AB%EB%B2%88%EC%A7%B8-%EC%98%88%EC%8B%9C_kiwi&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/Metacognition-Polymath/refactoring-2nd-edition/tree/master/01_%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81-%EC%B2%AB-%EB%B2%88%EC%A7%B8-%EC%98%88%EC%8B%9C/01_%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81_%EC%B2%AB%EB%B2%88%EC%A7%B8-%EC%98%88%EC%8B%9C_kiwi&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bT4KEQ/hyMKECabwK/0JsMsVUVzWj5KJMt572ph0/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;GitHub - Metacognition-Polymath/refactoring-2nd-edition: 코드 구조를 체계적으로 개선하여 효율적인 리팩터링&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;코드 구조를 체계적으로 개선하여 효율적인 리팩터링 구현하기. Contribute to Metacognition-Polymath/refactoring-2nd-edition development by creating an account on GitHub.&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>리팩터링 2판</category>
      <category>Refactoring</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/169</guid>
      <comments>https://coding-lks.tistory.com/169#entry169comment</comments>
      <pubDate>Mon, 20 Dec 2021 07:52:19 +0900</pubDate>
    </item>
    <item>
      <title>쿠키, 세션</title>
      <link>https://coding-lks.tistory.com/168</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;쿠키와 세션이 등장하게된 배경&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 배경을 이해하기 위해 HTTP의 특징에 대해 간단히 알아보자&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;HTTP의 특징&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;(1) Stateless (비상태성)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;  &lt;b&gt;Stateless 프로토콜이란?&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트의 상태 정보를 가지지 않는 서버 처리 방식&lt;/li&gt;
&lt;li&gt;클라이언트와 첫번째 통신에서 데이터를 주고 받았다 해도, 두번째 통신에서 이전 데이터를 유지하지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;(2) Connectionless (비연결성)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  &lt;b&gt;Connectionless 프로토콜이란?&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;클라이언트가 서버에 요청(Request)을 했을 때, 그 요청에 맞는 응답(Response)을 보낸 후 연결을 끊는 처리방식&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTTP 1.1 버전에서 연결을 유지하고, 재활용 하는 기능이 Default로 추가되었다. (Keep-alive 값으로 변경가능)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rArr; &lt;b&gt;그러나&lt;/b&gt;, 실제로 우리는 데이터 유지가 필요한 경우가 많다! : 정보가 유지되지 않으면, 매번 페이지를 이동할 때마다 로그인을 다시 하거나, 상품을 선택했는데 구매 페이지에서 선택한 상품의 정보가 없거나 하는 등의 일이 발생할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Connectionless 와 Stateless 덕분에 server의 자원은 많이 절약되지만, 같은 사용자가 여러 번 요청한 것을 매번 새로운 사용자로 인식하기 때문에, 페이지가 바뀔 때마다 계속 로그인을 해야 한다거나 장바구니에 담은 상품이 사라지는 등 치명적인 부작용이 발생한다. 이런 부작용을 보완하여 server 가 client 를 식별하게 해주는 것이 바로 Cookie와 Session이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;rArr; 따라서, Stateful한 경우를 대처하기 위해서 쿠키와 세션을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: 쿠키와 세션의 차이점은 크게 상태 정보의 저장 위치이다. 쿠키는 '클라이언트'(=로컬=사용자 브라우저)에 저장되고, 세션은 '서버'에 저장하는데 추가적인 것은 아래에서 자세히 살펴보도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;쿠키&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트(ex. 브라우저, 모바일 app 등)의 로컬에 저장되고, Http통신시 헤더에 포함시킬 수 있는 Key와 value로 구성된 작은 텍스트 데이터 파일을 말합니다.&lt;/li&gt;
&lt;li&gt;클라이언트에&amp;nbsp;300개까지&amp;nbsp;쿠키저장&amp;nbsp;가능,&amp;nbsp;하나의&amp;nbsp;도메인당&amp;nbsp;20개의&amp;nbsp;값만&amp;nbsp;가질&amp;nbsp;수&amp;nbsp;있음,&amp;nbsp;하나의&amp;nbsp;쿠키값은&amp;nbsp;4KB까지&amp;nbsp;저장. (도메인당 20개가 넘어가면 가장 적게 사용되는 것부터 삭제합니다.)&lt;/li&gt;
&lt;li&gt;쿠키는&amp;nbsp;클라이언트의&amp;nbsp;상태&amp;nbsp;정보를&amp;nbsp;로컬에&amp;nbsp;저장했다가&amp;nbsp;참조하기 위해 사용된다.&lt;/li&gt;
&lt;li&gt;Response&amp;nbsp;Header에&amp;nbsp;Set-Cookie&amp;nbsp;속성을&amp;nbsp;사용하면&amp;nbsp;클라이언트에&amp;nbsp;쿠키를&amp;nbsp;만들&amp;nbsp;수&amp;nbsp;있다.&lt;/li&gt;
&lt;li&gt;쿠키는 사용자가 따로 요청하지 않아도 브라우저가 요청(Request)시에 Request Header를 넣어 자동으로 서버에 전송합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;쿠키의 구성요소&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 key들로 구성되어 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이름&amp;nbsp;:&amp;nbsp;각각의&amp;nbsp;쿠키를&amp;nbsp;구별하는&amp;nbsp;데&amp;nbsp;사용되는&amp;nbsp;이름&lt;/li&gt;
&lt;li&gt;값&amp;nbsp;:&amp;nbsp;쿠키의&amp;nbsp;이름과&amp;nbsp;관련된&amp;nbsp;값&lt;/li&gt;
&lt;li&gt;유효시간&amp;nbsp;:&amp;nbsp;쿠키의&amp;nbsp;유지시간(지정가능)&lt;/li&gt;
&lt;li&gt;도메인&amp;nbsp;:&amp;nbsp;쿠키를&amp;nbsp;전송할&amp;nbsp;도메인&lt;/li&gt;
&lt;li&gt;경로&amp;nbsp;:&amp;nbsp;쿠키를&amp;nbsp;전송할&amp;nbsp;요청&amp;nbsp;경로&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;쿠키의 동작 방식&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최초 통신에서는 쿠키 값이 없으므로, 일단 클라이언트는 Request를 한다.&lt;/li&gt;
&lt;li&gt;서버에서 클라이언트가 보낸 Request Header에 쿠키가 없음을 판별, 쿠키 생성&lt;/li&gt;
&lt;li&gt;통신 상태(UserID, Password, 조작상태, 방문횟수 등)를 저장한 쿠키를 Response header에 담아 Response한다.&lt;/li&gt;
&lt;li&gt;클라이언트의 브라우저가 받은 쿠키를 생성/보존한다.&lt;/li&gt;
&lt;li&gt;두번째 연결부턴, HTTP Header에 쿠키를 실어서 서버에 Request 한다.&lt;/li&gt;
&lt;li&gt;만약 서버에서 쿠키를 읽어 상태 정보를 변경해야 한다면 쿠키를 업데이트하여 변경된 쿠키가 담긴 reponse를 보낸다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;쿠키 사용 예시&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;방문&amp;nbsp;사이트에서&amp;nbsp;로그인&amp;nbsp;시,&amp;nbsp;&quot;아이디와&amp;nbsp;비밀번호를&amp;nbsp;저장하시겠습니까?&quot; &amp;rarr; 쿠키 데이터를 로컬에 저장하겠냐고 묻는겁니다.&lt;/li&gt;
&lt;li&gt;쇼핑몰의&amp;nbsp;장바구니&amp;nbsp;기능&lt;/li&gt;
&lt;li&gt;자동로그인 / 유지&lt;/li&gt;
&lt;li&gt;팝업에서&amp;nbsp;&quot;오늘&amp;nbsp;더&amp;nbsp;이상&amp;nbsp;이&amp;nbsp;창을&amp;nbsp;보지&amp;nbsp;않음&quot;&amp;nbsp;체크&lt;/li&gt;
&lt;li&gt;위시 리스트 저장&lt;/li&gt;
&lt;li&gt;사용자 이전 스크롤링&lt;/li&gt;
&lt;li&gt;뷰 설정 값 등등...&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;세션이란?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버측에 저장, 관리되는 쿠키&lt;/li&gt;
&lt;li&gt;클라이언트와 서버의 통신 상태 등의 &lt;b&gt;주로 중요한 데이터를 저장 시에 사용&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;접속&amp;nbsp;시간에&amp;nbsp;제한을&amp;nbsp;두어&amp;nbsp;일정&amp;nbsp;시간&amp;nbsp;응답이&amp;nbsp;없다면&amp;nbsp;정보가&amp;nbsp;유지되지&amp;nbsp;않게&amp;nbsp;설정이&amp;nbsp;가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;세션의 동작 방식&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트가 서버에 접속 시, 세션 ID를 발급받습니다.&lt;/li&gt;
&lt;li&gt;클라이언트는 세션 ID에 대해 쿠키를 사용해서 저장합니다.&lt;/li&gt;
&lt;li&gt;클라리언트는 해당 서버(Host)에 요청할 때, 이 쿠키의 세션 ID를 서버에 전달합니다.&lt;/li&gt;
&lt;li&gt;서버는 세션 ID를 전달 받아서 별다른 작업없이 세션 ID로 세션에 있는 클라이언트를 판단, 클라이언트의 정보에 접근합니다.&lt;/li&gt;
&lt;li&gt;클라이언트 정보를 가지고 서버 요청을 처리하여 클라이언트에게 응답한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;세션의 동작 방식&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각&amp;nbsp;클라이언트에게&amp;nbsp;고유&amp;nbsp;ID를&amp;nbsp;부여&lt;/li&gt;
&lt;li&gt;세션&amp;nbsp;ID로&amp;nbsp;클라이언트를&amp;nbsp;구분해서&amp;nbsp;클라이언트의&amp;nbsp;요구에&amp;nbsp;맞는&amp;nbsp;서비스를&amp;nbsp;제공&lt;/li&gt;
&lt;li&gt;로그인 정보와 같은 개인 정보 등이 주로 저장되며, 클라이언트가 아닌 서버에 저장되기 때문에 쿠키보다 탈취가 어려움 &amp;rarr; 보안성이 비교적 높다.&lt;/li&gt;
&lt;li&gt;사용자가&amp;nbsp;많아질수록&amp;nbsp;서버&amp;nbsp;메모리를&amp;nbsp;많이&amp;nbsp;차지하게&amp;nbsp;되고 이로인해 서버에 부하를 주게되 성능 저하의 요인이 될 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;세션의 사용 예시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인 같이 보안상 중요한 작업을 수행할 때 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 쿠키와 세션의 비교&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (2).png&quot; data-origin-width=&quot;1015&quot; data-origin-height=&quot;260&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMNTbU/btrodmft3PS/s52CrVee52kFjJk6zj3751/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMNTbU/btrodmft3PS/s52CrVee52kFjJk6zj3751/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMNTbU/btrodmft3PS/s52CrVee52kFjJk6zj3751/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMNTbU%2Fbtrodmft3PS%2Fs52CrVee52kFjJk6zj3751%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;692&quot; height=&quot;177&quot; data-filename=&quot;Untitled (2).png&quot; data-origin-width=&quot;1015&quot; data-origin-height=&quot;260&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (3).png&quot; data-origin-width=&quot;665&quot; data-origin-height=&quot;245&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tazHk/btroc4Z1gra/HXMQ0sHFbdQqwwhsTdVGZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tazHk/btroc4Z1gra/HXMQ0sHFbdQqwwhsTdVGZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tazHk/btroc4Z1gra/HXMQ0sHFbdQqwwhsTdVGZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtazHk%2Fbtroc4Z1gra%2FHXMQ0sHFbdQqwwhsTdVGZ0%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;537&quot; height=&quot;198&quot; data-filename=&quot;Untitled (3).png&quot; data-origin-width=&quot;665&quot; data-origin-height=&quot;245&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;쿠키와&amp;nbsp;세션은&amp;nbsp;비슷한&amp;nbsp;역할을&amp;nbsp;하며,&amp;nbsp;동작원리도&amp;nbsp;비슷.&amp;nbsp;세션도&amp;nbsp;결국&amp;nbsp;쿠키를&amp;nbsp;사용하기&amp;nbsp;때문&lt;/li&gt;
&lt;li&gt;가장&amp;nbsp;큰&amp;nbsp;차이점은&amp;nbsp;사용자의 정보가&amp;nbsp;저장되는&amp;nbsp;위치.&amp;nbsp;쿠키는&amp;nbsp;서버의&amp;nbsp;자원을&amp;nbsp;전혀&amp;nbsp;사용하지&amp;nbsp;않으며,&amp;nbsp;세션은&amp;nbsp;서버의&amp;nbsp;자원을&amp;nbsp;사용합니다.&lt;/li&gt;
&lt;li&gt;보안&amp;nbsp;면에서&amp;nbsp;세션이&amp;nbsp;더&amp;nbsp;우수하며,&amp;nbsp;요청&amp;nbsp;속도는&amp;nbsp;쿠키가&amp;nbsp;세션보다&amp;nbsp;더&amp;nbsp;빠르다.&amp;nbsp;세션은&amp;nbsp;서버의&amp;nbsp;처리가&amp;nbsp;필요하기&amp;nbsp;때문&lt;/li&gt;
&lt;li&gt;보안,&amp;nbsp;쿠키는&amp;nbsp;클라이언트&amp;nbsp;로컬에&amp;nbsp;저장되기&amp;nbsp;때문에&amp;nbsp;변질되거나&amp;nbsp;request에서&amp;nbsp;스니핑&amp;nbsp;당할&amp;nbsp;우려가&amp;nbsp;있어서&amp;nbsp;보안에&amp;nbsp;취약하지만&amp;nbsp;세션은&amp;nbsp;쿠키를&amp;nbsp;이용해서&amp;nbsp;sessionid&amp;nbsp;만&amp;nbsp;저장하고&amp;nbsp;그것으로&amp;nbsp;구분해서&amp;nbsp;서버에서&amp;nbsp;처리하기&amp;nbsp;때문에&amp;nbsp;비교적&amp;nbsp;보안성이&amp;nbsp;좋다.&lt;/li&gt;
&lt;li&gt;라이프&amp;nbsp;사이클,&amp;nbsp;쿠키도&amp;nbsp;만료시간이&amp;nbsp;있지만&amp;nbsp;파일로&amp;nbsp;저장되기&amp;nbsp;때문에&amp;nbsp;브라우저를&amp;nbsp;종료해도&amp;nbsp;계속해서&amp;nbsp;정보가&amp;nbsp;남아&amp;nbsp;있을&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp;또한&amp;nbsp;만료기간을&amp;nbsp;넉넉하게&amp;nbsp;잡아두면&amp;nbsp;쿠키삭제를&amp;nbsp;할&amp;nbsp;때&amp;nbsp;까지&amp;nbsp;유지될&amp;nbsp;수도&amp;nbsp;있다.&lt;/li&gt;
&lt;li&gt;반면에&amp;nbsp;세션도&amp;nbsp;만료시간을&amp;nbsp;정할&amp;nbsp;수&amp;nbsp;있지만&amp;nbsp;브라우저가&amp;nbsp;종료되면&amp;nbsp;만료시간에&amp;nbsp;상관없이&amp;nbsp;삭제된다.&lt;/li&gt;
&lt;li&gt;속도,&amp;nbsp;쿠키에&amp;nbsp;정보가&amp;nbsp;있기&amp;nbsp;때문에&amp;nbsp;서버에&amp;nbsp;요청시&amp;nbsp;속도가&amp;nbsp;빠르고&amp;nbsp;세션은&amp;nbsp;정보가&amp;nbsp;서버에&amp;nbsp;있기&amp;nbsp;때문에&amp;nbsp;처리가&amp;nbsp;요구되어&amp;nbsp;비교적&amp;nbsp;느린&amp;nbsp;속도를&amp;nbsp;낸다.&lt;/li&gt;
&lt;li&gt;세션이 성능이 좋음에도 쿠키를 사용하는 이유는 세션은&amp;nbsp;서버의&amp;nbsp;자원을&amp;nbsp;사용하기때문에&amp;nbsp;무분별하게&amp;nbsp;만들다보면&amp;nbsp;서버의&amp;nbsp;메모리가&amp;nbsp;감당할&amp;nbsp;수&amp;nbsp;없어질&amp;nbsp;수가&amp;nbsp;있고&amp;nbsp;속도가&amp;nbsp;느려질&amp;nbsp;수&amp;nbsp;있기&amp;nbsp;때문&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참조 :&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@goblin820/HTTP-Cookie-vs-Session&quot;&gt;https://velog.io/@goblin820/HTTP-Cookie-vs-Session&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://pstudio411.tistory.com/entry/쿠키Cookie&quot;&gt;https://pstudio411.tistory.com/entry/쿠키Cookie&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hyojin96.tistory.com/entry/Cookie와-Session&quot;&gt;https://hyojin96.tistory.com/entry/Cookie와-Session&lt;/a&gt;&lt;/p&gt;</description>
      <category>web</category>
      <category>cookie</category>
      <category>session</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/168</guid>
      <comments>https://coding-lks.tistory.com/168#entry168comment</comments>
      <pubDate>Sun, 19 Dec 2021 00:52:19 +0900</pubDate>
    </item>
    <item>
      <title>Synchronous VS Asynchronous, blocking vs non-blocking</title>
      <link>https://coding-lks.tistory.com/167</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=oEIoqGd-Sns&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;멍토의 Blocking vs Non-Blocking, Sync vs Async&lt;/a&gt;&amp;nbsp;의 내용을 정리한 글입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Blocking VS Non-Blocking&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Blocking&lt;/b&gt; : 자신의 작업을 진행하다가 다른 주체의 작업이 시작되면 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;다른 작업이 &lt;/span&gt;&lt;span data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&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-filename=&quot;Screen_Shot_2021-06-24_at_1.16.52_PM.png&quot; data-origin-width=&quot;1184&quot; data-origin-height=&quot;972&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bj7ue0/btrob1pj6xQ/7v0KmkEsGdk3oP6GynXwXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bj7ue0/btrob1pj6xQ/7v0KmkEsGdk3oP6GynXwXK/img.png&quot; data-alt=&quot;Blocking예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bj7ue0/btrob1pj6xQ/7v0KmkEsGdk3oP6GynXwXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbj7ue0%2Fbtrob1pj6xQ%2F7v0KmkEsGdk3oP6GynXwXK%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;540&quot; height=&quot;443&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.16.52_PM.png&quot; data-origin-width=&quot;1184&quot; data-origin-height=&quot;972&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Blocking예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.17.56_PM.png&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;677&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PD30r/btrob7pFbWr/LkSXtktKKK0Rsmk6GBohlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PD30r/btrob7pFbWr/LkSXtktKKK0Rsmk6GBohlk/img.png&quot; data-alt=&quot;Blocking 설명 그림&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PD30r/btrob7pFbWr/LkSXtktKKK0Rsmk6GBohlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPD30r%2Fbtrob7pFbWr%2FLkSXtktKKK0Rsmk6GBohlk%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;536&quot; height=&quot;224&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.17.56_PM.png&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;677&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Blocking 설명 그림&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 호출자&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 호출된 함수A&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호출자는 함수A가 끝날때까지 기다리면서 다른일을 하지 못한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Non-Bolcing&lt;/b&gt; : 다른 주체의 작업에 &lt;span style=&quot;background-color: #9feec3;&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;관련없이&lt;/span&gt; 자신의 작업을 하는 것&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.20.13_PM.png&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;920&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvNPJ7/btrocTxGgEn/aKkKvzyPdTJTyl2b7Zk94k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvNPJ7/btrocTxGgEn/aKkKvzyPdTJTyl2b7Zk94k/img.png&quot; data-alt=&quot;Non-Blocking 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvNPJ7/btrocTxGgEn/aKkKvzyPdTJTyl2b7Zk94k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvNPJ7%2FbtrocTxGgEn%2FaKkKvzyPdTJTyl2b7Zk94k%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;535&quot; height=&quot;362&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.20.13_PM.png&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;920&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Non-Blocking 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호출자는 함수A를 호출하고 함수 A가 작업을 하는 순간 빠져나와 자기의 일을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.34.40_PM.png&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;657&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6qat8/btrogvvOujW/gCBCujuNSypmGoo3829T4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6qat8/btrogvvOujW/gCBCujuNSypmGoo3829T4K/img.png&quot; data-alt=&quot;Blocking 설명 그림&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6qat8/btrogvvOujW/gCBCujuNSypmGoo3829T4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6qat8%2FbtrogvvOujW%2FgCBCujuNSypmGoo3829T4K%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;535&quot; height=&quot;217&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.34.40_PM.png&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;657&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Blocking 설명 그림&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;호출자&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 호출된 함수A&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호출자는 함수A를 호출하고 작업을 하는 순간 빠져나와 자기의 일을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 주체가 작업할 때 자신의 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;제어권이&lt;/span&gt; 있는지 없는지로 구분해 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bolcking와 Non-Blocking은 호출되는 함수가 바로 return하느냐 마느냐가 관심사이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;synchronous VS Asynchronous&lt;/h4&gt;
&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;synchronous&lt;/b&gt; : 번역을 해보면 동기라는 뜻을 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업을 동시에 수행하거나, 동시에 끝나거나, &lt;span style=&quot;background-color: #9feec3;&quot;&gt;끝나는 동시에 시작&lt;/span&gt;함을 의미합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.39.22_PM.png&quot; data-origin-width=&quot;1340&quot; data-origin-height=&quot;970&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOFzTS/btrocp4E1mU/yaZ6cwjNLlMaLxH8X2XHlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOFzTS/btrocp4E1mU/yaZ6cwjNLlMaLxH8X2XHlk/img.png&quot; data-alt=&quot;synchronous 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOFzTS/btrocp4E1mU/yaZ6cwjNLlMaLxH8X2XHlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOFzTS%2Fbtrocp4E1mU%2FyaZ6cwjNLlMaLxH8X2XHlk%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;541&quot; height=&quot;392&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.39.22_PM.png&quot; data-origin-width=&quot;1340&quot; data-origin-height=&quot;970&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;synchronous 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.41.39_PM.png&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;643&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2XPmo/btrodS59Dlo/erfmpxoZDZ1aa2iusg7Ktk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2XPmo/btrodS59Dlo/erfmpxoZDZ1aa2iusg7Ktk/img.png&quot; data-alt=&quot;synchronous&amp;amp;amp;amp;amp;amp;nbsp;설명 그림&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2XPmo/btrodS59Dlo/erfmpxoZDZ1aa2iusg7Ktk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2XPmo%2FbtrodS59Dlo%2FerfmpxoZDZ1aa2iusg7Ktk%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;532&quot; height=&quot;211&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.41.39_PM.png&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;643&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;synchronous&amp;amp;amp;amp;amp;nbsp;설명 그림&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;호출자&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;호출된 함수A&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;호출자는 함수A를 호출하고 호출자가 기다리거나 다른일을 하는 것을 신경 쓰지 않습니다.&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;호출자는 함수A에게 결과값을 반환받고 &lt;span style=&quot;background-color: #9feec3;&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;바로 처리&lt;/span&gt;합니다.(결과값을 반환받고 처리하는 &lt;span style=&quot;background-color: #9feec3;&quot; data-token-index=&quot;3&quot; data-reactroot=&quot;&quot;&gt;시점&lt;/span&gt;에 의미를 두세요)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;822&quot; data-origin-height=&quot;228&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c3LYU2/btrocTkaOpa/rjcU5dkKJssiYquxv2ywN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c3LYU2/btrocTkaOpa/rjcU5dkKJssiYquxv2ywN1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c3LYU2/btrocTkaOpa/rjcU5dkKJssiYquxv2ywN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc3LYU2%2FbtrocTkaOpa%2FrjcU5dkKJssiYquxv2ywN1%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;472&quot; height=&quot;131&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;822&quot; data-origin-height=&quot;228&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Thread1이 작업을 시작 시키고,&amp;nbsp;Task1이 끝날때까지 기다렸다&amp;nbsp;Task2를 시작한다.&lt;/li&gt;
&lt;li&gt;작업 요청을 했을 때 요청의&amp;nbsp;&lt;b&gt;결과값(return)을 직접 받는 것이다&lt;/b&gt;.&lt;/li&gt;
&lt;li&gt;요청의 결과값이 return값과 동일하다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Asynchronous : 번역을 해보면 비동기라는 뜻을 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작, 종료가 일치하지 않으며, &lt;span style=&quot;background-color: #9feec3;&quot;&gt;끝나는 동시에 시작을 하지 않음&lt;/span&gt;을 의미합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.43.16_PM.png&quot; data-origin-width=&quot;1314&quot; data-origin-height=&quot;924&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzynzT/btrodlAIcsp/IFzf6bNKuWE4GjoCbDdKn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzynzT/btrodlAIcsp/IFzf6bNKuWE4GjoCbDdKn1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzynzT/btrodlAIcsp/IFzf6bNKuWE4GjoCbDdKn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzynzT%2FbtrodlAIcsp%2FIFzf6bNKuWE4GjoCbDdKn1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;352&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.43.16_PM.png&quot; data-origin-width=&quot;1314&quot; data-origin-height=&quot;924&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.44.33_PM.png&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;636&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYPYPs/btrocSS6Vra/Lkwu4KhUzGWqaYOllE6tg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYPYPs/btrocSS6Vra/Lkwu4KhUzGWqaYOllE6tg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYPYPs/btrocSS6Vra/Lkwu4KhUzGWqaYOllE6tg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYPYPs%2FbtrocSS6Vra%2FLkwu4KhUzGWqaYOllE6tg1%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;496&quot; height=&quot;195&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.44.33_PM.png&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;636&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; 호출자&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;호출된 함수A&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호출자는 함수A를 호출하고 호출자가 기다리거나 다른일을 하는 것을 신경 쓰지 않습니다.&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;호출자는 함수A에게 결과값을 반환받고 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;언젠가 처리&lt;/span&gt;합니다.(결과값을 반환받고 처리하는 시점에 의미를 두세요)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 돌려주었을 때 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;순서와 결과에 관심&lt;/span&gt;이 있는지 아닌지로 판단할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 그룹은 호출되는 함수의 작업 완료 여부를 누가 신경쓰느냐가 관심사이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (1).png&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n0BIw/btrocUcm6Zv/pkcwRmwlE2m2JQNPbvtzak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n0BIw/btrocUcm6Zv/pkcwRmwlE2m2JQNPbvtzak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n0BIw/btrocUcm6Zv/pkcwRmwlE2m2JQNPbvtzak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn0BIw%2FbtrocUcm6Zv%2FpkcwRmwlE2m2JQNPbvtzak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;527&quot; height=&quot;131&quot; data-filename=&quot;Untitled (1).png&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;200&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Thread1이 작업을 시작 시키고, 완료를 기다리지 않고,&amp;nbsp;Thread1은 다른 일을 처리할 수 있다.&lt;/li&gt;
&lt;li&gt;작업 요청을 했을 때 요청의&amp;nbsp;&lt;b&gt;결과값(return)을 간접적으로 받는 것&lt;/b&gt;이다.&lt;/li&gt;
&lt;li&gt;요청의 결과값이 return값과 다를 수 있다.&lt;/li&gt;
&lt;li&gt;해당 요청 작업은 별도의 스레드에서 실행하게 된다.&lt;/li&gt;
&lt;li&gt;콜백을 통한 처리가 비동기 처리라고 할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;호출된 함수(callback 함수)가 작업 완료를 신경 쓴다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4가지의 조합&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.46.51_PM.png&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;732&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qQ8Q3/btrodlU4Vd6/4LrIwYBHDiL9AgyMM6e8vK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qQ8Q3/btrodlU4Vd6/4LrIwYBHDiL9AgyMM6e8vK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qQ8Q3/btrodlU4Vd6/4LrIwYBHDiL9AgyMM6e8vK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqQ8Q3%2FbtrodlU4Vd6%2F4LrIwYBHDiL9AgyMM6e8vK%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;533&quot; height=&quot;241&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.46.51_PM.png&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;732&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Block/Sync&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Blocking의 관점은 제어권에 있습니다 &amp;rarr; 그래서 한 작업이 동작할 때 다른 작업은 동작하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sync의 관점은 결과의 처리입니다. &amp;rarr; 그래서 kernel에서 결과를 반환하면 해당 업무를 application에서 바로 처리하게 됩니다.&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 style=&quot;list-style-type: none;&quot;&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.47.28_PM.png&quot; data-origin-width=&quot;1332&quot; data-origin-height=&quot;922&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/80eKQ/btrogxmTyJI/jMP6TvzEorkfUmpAhX9Gs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/80eKQ/btrogxmTyJI/jMP6TvzEorkfUmpAhX9Gs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/80eKQ/btrogxmTyJI/jMP6TvzEorkfUmpAhX9Gs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F80eKQ%2FbtrogxmTyJI%2FjMP6TvzEorkfUmpAhX9Gs1%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;575&quot; height=&quot;398&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.47.28_PM.png&quot; data-origin-width=&quot;1332&quot; data-origin-height=&quot;922&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.49.17_PM.png&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;711&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b51zr0/btroc4yRRqX/Ua15Y8dFGiIE3YJ8J8MAik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b51zr0/btroc4yRRqX/Ua15Y8dFGiIE3YJ8J8MAik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b51zr0/btroc4yRRqX/Ua15Y8dFGiIE3YJ8J8MAik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb51zr0%2Fbtroc4yRRqX%2FUa15Y8dFGiIE3YJ8J8MAik%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;568&quot; height=&quot;249&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.49.17_PM.png&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;711&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.49.38_PM.png&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;509&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1eKkd/btrofGYjMVf/Cf81Cdym6BXyHaVecNMqP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1eKkd/btrofGYjMVf/Cf81Cdym6BXyHaVecNMqP0/img.png&quot; data-alt=&quot;ex) java에서 입력 요청을 할때&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1eKkd/btrofGYjMVf/Cf81Cdym6BXyHaVecNMqP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1eKkd%2FbtrofGYjMVf%2FCf81Cdym6BXyHaVecNMqP0%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;580&quot; height=&quot;182&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.49.38_PM.png&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;509&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ex) java에서 입력 요청을 할때&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Non-Blocking/Sync&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Non-Blocking의 관점은 제어권에 있습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 그래서 한 작업이 동작할 때 다른 작업은 동작합니다(결과가 나왔는지 계속 물어보는 동작) 아직 작업이 끝나지 않았다는 결과를 계속 반환해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sync의 관점은 결과의 처리입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 그래서 kernel에서 결과를 반환하면 해당 업무를 application에서 바로 처리하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.50.35_PM.png&quot; data-origin-width=&quot;1318&quot; data-origin-height=&quot;952&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VYZUt/btroezyvSoS/N1pKN8kQbDeh7ekGdEzbVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VYZUt/btroezyvSoS/N1pKN8kQbDeh7ekGdEzbVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VYZUt/btroezyvSoS/N1pKN8kQbDeh7ekGdEzbVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVYZUt%2FbtroezyvSoS%2FN1pKN8kQbDeh7ekGdEzbVK%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;484&quot; height=&quot;350&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.50.35_PM.png&quot; data-origin-width=&quot;1318&quot; data-origin-height=&quot;952&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.53.34_PM.png&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;645&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ctbA3v/btroeBiLOO7/1R24cnGZlNVQ9UsOZsrh10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ctbA3v/btroeBiLOO7/1R24cnGZlNVQ9UsOZsrh10/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ctbA3v/btroeBiLOO7/1R24cnGZlNVQ9UsOZsrh10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FctbA3v%2FbtroeBiLOO7%2F1R24cnGZlNVQ9UsOZsrh10%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;478&quot; height=&quot;190&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.53.34_PM.png&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;645&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.54.29_PM.png&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;672&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcfTKx/btroi6vzekW/IOuD0xueosaBEnygwtO48K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcfTKx/btroi6vzekW/IOuD0xueosaBEnygwtO48K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcfTKx/btroi6vzekW/IOuD0xueosaBEnygwtO48K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcfTKx%2Fbtroi6vzekW%2FIOuD0xueosaBEnygwtO48K%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;482&quot; height=&quot;200&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.54.29_PM.png&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;672&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Blocking/Async&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Blocking의 관점은 제어권에 있습니다 &amp;rarr; 그래서 한 작업이 동작할 때 다른 작업은 동작하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ASync의 관점은 결과의 처리입니다. &amp;rarr; 그래서 결과를 바로 처리하지 않아도 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.56.02_PM.png&quot; data-origin-width=&quot;1320&quot; data-origin-height=&quot;910&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7Q6pu/btrob1Xd0YY/qsqsi9Dki9YWl3mU9dk201/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7Q6pu/btrob1Xd0YY/qsqsi9Dki9YWl3mU9dk201/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7Q6pu/btrob1Xd0YY/qsqsi9Dki9YWl3mU9dk201/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7Q6pu%2Fbtrob1Xd0YY%2Fqsqsi9Dki9YWl3mU9dk201%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;501&quot; height=&quot;346&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.56.02_PM.png&quot; data-origin-width=&quot;1320&quot; data-origin-height=&quot;910&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;굳이 이렇게 해야할까? &amp;rarr; 개발자의 실수 or 기타이유로 이런 코드가 작성되는 경우가 있다고 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.56.55_PM.png&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;713&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dAdUeA/btroaLtKI2D/23DLVKQofeZVH7topwYXN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dAdUeA/btroaLtKI2D/23DLVKQofeZVH7topwYXN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dAdUeA/btroaLtKI2D/23DLVKQofeZVH7topwYXN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdAdUeA%2FbtroaLtKI2D%2F23DLVKQofeZVH7topwYXN0%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;509&quot; height=&quot;224&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.56.55_PM.png&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;713&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Non-Blocking/Async&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Non-Blocking의 관점은 제어권에 있습니다 &amp;rarr; 그래서 한 작업이 동작할 때 다른 작업은 동작합니다(결과가 나왔는지 계속 물어보는 동작) 아직 작업이 끝나지 않았다는 결과를 계속 반환해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ASync의 관점은 결과의 처리입니다. &amp;rarr; 그래서 결과를 바로 처리하지 않아도 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.57.53_PM.png&quot; data-origin-width=&quot;1278&quot; data-origin-height=&quot;926&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqCF7A/btroceB3brJ/z1d4CdKH1Z7Z1nKc9dB8B0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqCF7A/btroceB3brJ/z1d4CdKH1Z7Z1nKc9dB8B0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqCF7A/btroceB3brJ/z1d4CdKH1Z7Z1nKc9dB8B0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqCF7A%2FbtroceB3brJ%2Fz1d4CdKH1Z7Z1nKc9dB8B0%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;540&quot; height=&quot;391&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_1.57.53_PM.png&quot; data-origin-width=&quot;1278&quot; data-origin-height=&quot;926&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자기 일이 끝나면 받은 결과를 처리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_2.00.03_PM.png&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;638&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eeAqlO/btrocTqVoRO/IXpRYTdgRQTfkIGy4j6xiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eeAqlO/btrocTqVoRO/IXpRYTdgRQTfkIGy4j6xiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eeAqlO/btrocTqVoRO/IXpRYTdgRQTfkIGy4j6xiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeeAqlO%2FbtrocTqVoRO%2FIXpRYTdgRQTfkIGy4j6xiK%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;533&quot; height=&quot;210&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_2.00.03_PM.png&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;638&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 예시로 자바스크립트에서 API요청을 하고 다른 작업을 하다가 콜백을 통해서 추가적인 작업을 처리를 할때가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;정리&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_2.01.21_PM.png&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;592&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EajQ2/btrob0DZeEs/MOZclkCi1CKqfXGjaRKwQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EajQ2/btrob0DZeEs/MOZclkCi1CKqfXGjaRKwQ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EajQ2/btrob0DZeEs/MOZclkCi1CKqfXGjaRKwQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEajQ2%2Fbtrob0DZeEs%2FMOZclkCi1CKqfXGjaRKwQ1%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;567&quot; height=&quot;207&quot; data-filename=&quot;Screen_Shot_2021-06-24_at_2.01.21_PM.png&quot; data-origin-width=&quot;1620&quot; data-origin-height=&quot;592&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;호출된 함수가&amp;nbsp;&lt;b&gt;바로 return해서 호출한 함수에게 제어권을 넘겨주고호출한 함수가 다른 일을 할 수 있는 기회&lt;/b&gt;를 줄 수 있으면 non-blocking이다.&lt;/li&gt;
&lt;li&gt;호출된 함수가&amp;nbsp;&lt;b&gt;자신의 작업을 모두 마칠 때까지&lt;/b&gt;호출한 함수에게&amp;nbsp;&lt;b&gt;제어권을 넘겨주지 않고 대기&lt;/b&gt;하게 만든다면 blocking이다.&lt;/li&gt;
&lt;li&gt;호출되는 함수에게&amp;nbsp;&lt;b&gt;callback&lt;/b&gt;을 전달해서 호출되는 함수의 작업이 완료되면호출되는 함수가 전달받은 callback을 실행하고,&amp;nbsp;&lt;b&gt;호출한 함수는 작업 완료 여부를 신경쓰지 않는다면&lt;/b&gt;&amp;nbsp;비동기이다.&lt;/li&gt;
&lt;li&gt;호출하는 함수가 호출되는 함수의 작업 완료 후&amp;nbsp;&lt;b&gt;return을 기다리거나&lt;/b&gt;호출되는 함수로부터 바로 return 받더라도&amp;nbsp;&lt;b&gt;작업 완료 여부를 호출한 함수 스스로 확인&lt;/b&gt;하며 신경 쓴다면 동기이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;예시&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;상황: 급하게 알아야 하는 답을 누군가에게 물어봐야하는 상황&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전화로 물어봐서 즉답을 얻는다. = 동기 요청처리&lt;/li&gt;
&lt;li&gt;이메일로 물어보고 메일 송신을 완료(return)했지만 답은 언제 올지 모른다. = 비동기 요청처리&lt;/li&gt;
&lt;li&gt;전화를 했는데 상대방이 너무 바빠 전화를 받지 않음전화를 받을때까지 계속 대기 = 동기 + 블록킹&lt;/li&gt;
&lt;li&gt;전화를 했는데 안 받음끊었다가 나중에 다시 전화함계속 반복했다가 어느 순간에 받아서 답을 얻음 = 동기 + 논블록킹&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;java: block, sync&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;js : non-block, async&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출처 : &lt;a href=&quot;https://www.youtube.com/watch?v=oEIoqGd-Sns&quot;&gt;우아한 테코톡&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@wonhee010/동기vs비동기-feat.-blocking-vs-non-blocking&quot;&gt;https://velog.io/@wonhee010/동기vs비동기-feat.-blocking-vs-non-blocking&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>etc</category>
      <category>asynchronous</category>
      <category>blocking</category>
      <category>non-blocking</category>
      <category>synchronous</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/167</guid>
      <comments>https://coding-lks.tistory.com/167#entry167comment</comments>
      <pubDate>Sun, 12 Dec 2021 22:10:42 +0900</pubDate>
    </item>
    <item>
      <title>web, api, rest</title>
      <link>https://coding-lks.tistory.com/166</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;웹(Web)의 개념&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;월드 와이드 웹(World Wide Web)이란 인터넷에 연결된 사용자 들이 서로의 정보를 공유할 수 있는 공간을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단히 줄여서 WWW나 W3라고도 부르며, 간단히 웹(Web)라고 가장 많이 불려집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터넷과 같은 의미로 많이 사용되고 있지만, 정확히 말해 웹은 인터넷 상의 인기 있는 하나의 서비스일 뿐입니다.&lt;br /&gt;하지만 현재에는 인터넷과 웹이라는 단어가 서로 혼용되어 사용될 만큼 인터넷의 가장 큰 부분을 차지하고 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.notion.so/image/http%3A%2F%2Ftcpschool.com%2Flectures%2Fimg_webbasic_03.jpg?table=block&amp;amp;id=5732d341-4c37-49c1-9b88-351267eb6f8d&amp;amp;spaceId=369af18f-df15-4af4-a82a-0a8b815a6145&amp;amp;width=2000&amp;amp;userId=0e68323f-a4ad-4d90-af0a-a6cc7414e4eb&amp;amp;cache=v2&quot; alt=&quot;&quot; width=&quot;521&quot; height=&quot;368&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;웹(Web)의 특징&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹은 인터넷 상에서 텍스트나 그림, 소리, 영상 등과 같은 멀티미디어 정보를 하이퍼텍스트 방식을 연결하여 제공합니다.&lt;br /&gt;하이퍼텍스트(hypertext)란 문서 내부에 또 다른 문서로 연결되는 참조를 집어 넣음으로서 웹 상에 존재하는 여러 문서끼리 서로 참조할 수 있는 기술을 의미합니다.&lt;br /&gt;이 때 문서 내부에서 또 다른 문서로 연결되는 참조를 하이퍼링크(hyperlink)라고 부릅니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.notion.so/image/http%3A%2F%2Ftcpschool.com%2Flectures%2Fimg_webbasic_04.png?table=block&amp;amp;id=4268b4dc-f525-4e92-8f2b-e50cda663313&amp;amp;spaceId=369af18f-df15-4af4-a82a-0a8b815a6145&amp;amp;width=2000&amp;amp;userId=0e68323f-a4ad-4d90-af0a-a6cc7414e4eb&amp;amp;cache=v2&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹에서는 HTML이라는 언어를 사용하여 누구나 자신만의 문서를 작성할 수 있습니다.&lt;br /&gt;또한, 이렇게 작성된 웹상의 문서에는 Http라는 프로토콜을 사용하면 누구나 검색하고 접근할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;웹(Web)의 구성&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹에서는 HTML언어를 사용하여 작성된 하이퍼텍스트 문서를 웹 페이지(web page)라고 부릅니다.&lt;br /&gt;이러한 웹 페이지들 중에서 서로 관련된 내용으로 작성된 웹 페이지들의 집합을 웹 사이트(web site)라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹은 이렇게 작성된 수많은 웹 페이지들이 하이퍼링크를 통해 서로 연결되어 구성됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 웹페이지에 포함된 하이퍼링크를 따라 다른 웹 페이지들로 계속하여 이동하는 것을 웹 서핑(web surfing)이라고 부르며, 이 때 사용자가 웹 페이지를 검색하기 위해 사용하는 프로그램을 웹 브라우저(web browser)라고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흔히 웹 사이트(web site)와 홈페이지(home page)라는 용어를 같은 의미로 사용하는 경우가 많습니다.&lt;br /&gt;사실 홈페이지(homepage)의 정확한 의미는 익스플로러나 크롬과 같은 웹 브라우저가 시작될 때 맨 처음 자동으로 표시되는 웹 페이지나 웹 브라우저의 홈 버튼을 누를 때 이동되는 웹 페이지를 지칭합니다.&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;h3 data-ke-size=&quot;size23&quot;&gt;정리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;web이란 인터넷에 연결된 사용자들이 서로의 정보를 공유할 수 있는 공간을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터넷상의 인기 있는 하나의 서비스입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멀티미디어 정보(텍스트, 그림, 소리, 영상 등고 ㅏ같은 멀티미디어 정보를 하이퍼텍스트 방식으로 연결하여 제공합니다.)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하이퍼 텍스트란?&lt;br /&gt;문서 내부에 또 다른 문서로 연결되는 참조를 넣음으로써 웹 상에 존재하는 여러 문서끼리 서로 참조할 수 있는 기술&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Web Api란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP 프로토콜을 사용하여 액세스 할 수있는 웹을 통한 API &amp;rarr; 기술이 아니라 개념입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;REST Api란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;rest라는 것은 웹 서비스에서 많이 사용되는데 application사이에 결합도를 낮추게끔 설계하는 아키텍처 스타일&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다양한 클라이언트들에게 정보를 제공하는 방법을 하나로 정하기 위해 web api + 여러가지 제약조건(rest 아키텍쳐를 따르는)이 추가된 API입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;탄생 배경&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;웹을 망가뜨리지 않고 어떻게 http 기능을 증가시킬 수 있을까?&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;REST를 구성하는 스타일 6가지&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Client - Server(클라이언트와 서버의 구조)&lt;br /&gt;REST 서버는 API를 제공합니다.&lt;br /&gt;하나의 REST API를 여러 클라이언트에서 이용할 수 있습니다.&lt;br /&gt;서버(Rest Server)에서는 비즈니스 로직 처리 및 Data 저장을 책임집니다.&lt;br /&gt;클라이언트의 경우 사용자 인증이나 컨텍스트(세션, 로그인 정보&lt;br /&gt;) 등을 직접 관리하고 책임지는 구조로 서버와 클라이언트의 역할이 나뉘어 지고 잇습니다.&lt;/li&gt;
&lt;li&gt;Stateless(무상태성)&lt;br /&gt;REST는 상태 정보를 따로 저장, 관리하지 않습니다.&lt;br /&gt;세션이나 쿠키 정보를 별도로 저장, 관리하지 않으므로 api서버는 들어오는 request만 단순 처리하면 됩니다.&lt;br /&gt;이는 곧 서비스의 자유도가 높아지고 서버에 불필요한 정보를 관리하지 않으므로 구현이 간결해집니다.&lt;/li&gt;
&lt;li&gt;ex) 로그인 - 서버에서 클라이언트의 상태를 저장, 관리하지 않는다.(jwt와 같은 기술을 사용하여 관리)&lt;/li&gt;
&lt;li&gt;Cache (캐시 가능)&lt;br /&gt;REST의 가장 큰 특징은 HTTP라는 기존의 웹 표준을 그대로 따른다는 것입니다.&lt;br /&gt;이 말은 곧, 기존의 웹 자원들을 그대로 활용할 수 있다는 점입니다.&lt;br /&gt;따라서 HTTP가 가진 캐싱(임시 저장) 기능이 적용가능합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; start=&quot;4&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Uniform (유니폼 인터페이스)&lt;br /&gt;rest는 HTTP표준에만 따른다면, 안드로이드/IOS 플랫폼이든, 특정 언어나 기술에 종속되지 않고 모든 플랫폼에 사용할 수 있으며, URI로 지정한 리소스에 대한 조작이 가능한 아키텍처 스타일을 의미한다.&lt;/li&gt;
&lt;li&gt;layered system(계층형 구조)&lt;br /&gt;rest 서버는 다중 계층으로 구성될 수 있으며 보안, 로드 밸런싱, 암호화 계층을 추가해 구조상의 유연성을 둘 수 있고 PROXY, 게이트웨이 같은 네트워크 기반의 중간매체를 사용할 수 있게 한다.&lt;/li&gt;
&lt;li&gt;-&amp;gt; RESTful한 서버는 보안, 로드밸런싱, 인증 등의 여러 계층으로 구성될 수 있다.(모듈화된 프로그램을 만들 듯이 각 계층을 분리시켜서 좀 더 오류를 찾기 쉽고, 관리 하기 쉽도록 하기 위해서라고 이해했습니다.)&lt;br /&gt;클라이언트는 server의 정해진 url에 요청을 보내기만 하면 되고, 서버는 다양한 layer를 이용해 작업할 수 있다.&lt;/li&gt;
&lt;li&gt;Code-On-Demand (선택적 제약 조건)&lt;br /&gt;서버로 부터 스크립트를 받아서 Client에서 실행하는 것입니다.반드시 충족할 필요는 없습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;uniform interface를 만족시키는 4가지 조건&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) identification of resources&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;Resource가 URI로 식별되야한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2) manipulation of resources through representations&lt;/b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;representations 전송을 통해서 resources를 조작해야한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;representations이란 HTTP 메소드의 GET, POST, PUT, DELETE 등을 말한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대상 정보의 자원
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GET /members: members의 모든 정보가 대상&lt;/li&gt;
&lt;li&gt;DELETE /members/1: members 중 1이 대상&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;자원에 대한 행위
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;올바른 표현: GET /members/1, POST /members, PUT /members/1, DELETE /members/1&lt;/li&gt;
&lt;li&gt;틀린 표현: GET /members/get/1, GET /members/add, GET /members/update/1, GET /members/del/1
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;조회, 입력, 수정, 삭제(get, post, put, delete)와 관련된 명령이 중간에 동사로 중첩되어 표현되면 안 됨(http 메소드를 동사로 사용하기 때문)&lt;/li&gt;
&lt;li&gt;어떤 행위를 해야 하는지가 HTTP 메서드로 명확히 전달되어야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3) Self-descriptive messages&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;메시지는 스스로를 설명해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시를 봅시다!!&lt;/p&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;GET / HTTP/1.1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 메시지는 단순히 root를 가져오는 GET요청입니다.&lt;br /&gt;이 HTTP 요청 메시지는 무언가가 빠져있어 Self-descriptive하지 못합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;http 버젼과 메소드는 명시되어 있지만 목적지가 빠져있다.&lt;br /&gt;이 요청이 어느 도메인 or ip주소를 가진 Host로 간다라는 목적지가 빠져있기 때문에 이런 경우는 Self-descriptive하지 않다고 합니다!&lt;/p&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;GET / HTTP/1.1
Host: www.example.org&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가적으로 이런 경우도 생각할수 있습니다.&lt;br /&gt;아래는 상태코드 200의 response 메시지이며, JSON 본문이 있습니다.&lt;/p&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;HTTP/1.1 200 OK

[ { &quot;op&quot;: &quot;remove&quot;, &quot;path&quot;: &quot;/a/b/c&quot; } ]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 되면 당연히 Self-descriptive하지 않는데&lt;br /&gt;클라이언트 입장에서 이 response를 해석할 때 어떤 문법으로 작성된 것인지 모르기 때문에 해석을 할 수 없기 때문이다.&lt;br /&gt;그렇기 때문에 Content-Type 헤더가 반드시 들어가야한다.&lt;/p&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;HTTP/1.1 200 OK
Content-Type: application/json

[ { &quot;op&quot;: &quot;remove&quot;, &quot;path&quot;: &quot;/a/b/c&quot; } ]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 까지 작성 됬다면 대괄호, 중괄호, 큰따옴표가 뭘 의미하는지 알게 되어, 파싱이 가능하여 문법을 해석할 수 있게 된다.&lt;br /&gt;그럼 이제 Self-descriptive하다고 할 수 있을까? NO!!! (여기까지가 대부분의 restful api가 지키는 수준)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 json data를 파싱했다고 하더라도, op key는 무슨 뜻이고, path의 값은 어떤걸 의미하는 건지 알 수 없다.(안 적혀 있으니까)&lt;/p&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;HTTP/1.1 200 OK
Content-Type: application/json-patch+json

[ { &quot;op&quot;: &quot;remove&quot;, &quot;path&quot;: &quot;/a/b/c&quot; } ]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 patch+json와 같은 미디어 타입까지 정의된 메시지라면 json_patch라는 명세를 찾아보고 이해한 다음, 명세에 따라 이 메시지를 해석하고 그제서야 올바르게 메시지의 의미를 이해할 수 있게 됩니다.&lt;br /&gt;하지만 오늘날의 REST API는 상당히 이를 만족하지 못하고 있다. 대부분 미디어 타입에는 그냥 json이라고만 되어있고 이를 어떻게 해석해야되는지는 명시가 되어있지 않다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;http://jsonpatch.com/&quot;&gt;http://jsonpatch.com/&lt;/a&gt; - 이런 식으로 공식 문서에 명시되어 있는 명세들&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.iana.org/assignments/media-types/media-types.xhtml&quot;&gt;IANA&lt;/a&gt; 에서 미디어타입 등록도 가능합니다. - 사용하는 인원이 소수이고 모두 숙지, 이해하고있다면 등록 필요 X&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4) HATEOAS&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플리케이션의 상태는 Hyperlink를 이용해 전이되어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래와 같은 사이트가 있다고 해보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://media.vlpt.us/images/kjh03160/post/edbf8e1f-7e61-411c-a1aa-744afb19638f/image.png&quot; alt=&quot;https://media.vlpt.us/images/kjh03160/post/edbf8e1f-7e61-411c-a1aa-744afb19638f/image.png&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;루트 홈페이지 &amp;rarr; 글 목록 보기 GET &amp;rarr; 글 쓰기 GET &amp;rarr; 글 저장 POST &amp;rarr; 생성된 글 보기 GET &amp;rarr; 목록 보기 GET &amp;rarr; ...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 상태를 전이하는 것을 애플리케이션 상태 전이라고 하고, 이 &lt;b&gt;상태 전이마다 항상 해당 페이지에 있던 링크를 따라가면서 전이했기 때문에 HATEOAS&lt;/b&gt;라고 할 수 있다. 말 그대로, 하이퍼 링크를 통한 전이가 되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 html 같은 경우를 보면 HATEOAS를 만족하게 되는데,&lt;/p&gt;
&lt;pre class=&quot;http&quot;&gt;&lt;code&gt;HTTP/1.1 200 OK
Content-Type: text/html

&amp;lt;html&amp;gt;&amp;lt;head&amp;gt; &amp;lt;/head&amp;gt;&amp;lt;body&amp;gt; &amp;lt;a href=&quot;/test&quot;&amp;gt; test &amp;lt;/a&amp;gt; &amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;a 태그를 통해서 하이퍼링크가 나와 있고, 이 하이퍼 링크를 통해서 그 다음 상태로 전이가 가능하기 때문에 만족한다고 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Json으로 작성한다면?&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;HTTP/1.1 200 OK
Content-Type: application/json
Link: &amp;lt;/articles/1&amp;gt;; rel=&quot;previous&quot;,
            &amp;lt;/articles/3&amp;gt;; rel=&quot;next&quot;;

{
    &quot;title&quot;: &quot;The second article&quot;,
    &quot;contents&quot;: &quot;blah blah...&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 Link 헤더를 활용하여 작성할 수 있습니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;정리&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오늘날 대부분 &quot;REST API&quot;는 사실 REST를 따르고 있지 않다.&lt;/li&gt;
&lt;li&gt;REST의 제약 조건 중 특히 &lt;b&gt;self-descriptive와 HATEOAS&lt;/b&gt;를 잘 만족하지 못한다.&lt;/li&gt;
&lt;li&gt;REST를 따를 것인지는 API 설계하는 이들이 스스로 판단하여 결정&lt;/li&gt;
&lt;li&gt;REST를 따르겠다면 &lt;b&gt;Self-decriptive와 HATEOAS&lt;/b&gt;를 만족시켜야한다. &amp;rarr; 가장 많이 안지켜지는 두 가지입니다!!
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Self-decriptiv는 &lt;b&gt;custom media type, profile link relation&lt;/b&gt;으로 만족 가능&lt;/li&gt;
&lt;li&gt;HATEOAS는 &lt;b&gt;HTTP 헤더&lt;/b&gt;나 본문에 &lt;b&gt;링크&lt;/b&gt;를 담아 만족 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;REST를 따르지 않겠다면, &quot;REST를 만족하지 않는 REST API&quot;를 뭐라고 부를지 결정해야할 것이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HTTP API라고 부를 수도 있고&lt;/li&gt;
&lt;li&gt;그냥 이대로 REST API라고 부를 수도 &amp;rarr; 로이 필딩이 싫어합니다!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출처 :&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@kjh03160/%EA%B7%B8%EB%9F%B0-REST-API%EB%A1%9C-%EA%B4%9C%EC%B0%AE%EC%9D%80%EA%B0%80&quot;&gt;https://velog.io/@kjh03160/그런-REST-API로-괜찮은가&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/questions/126743&quot;&gt;https://www.inflearn.com/questions/126743&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;http://www.incodom.kr/REST#h_565e9294ea899706609e2ae2e849f76e&quot;&gt;http://www.incodom.kr/REST#h_565e9294ea899706609e2ae2e849f76e&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://doitnow-man.tistory.com/96&quot;&gt;https://doitnow-man.tistory.com/96&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://til-devsong.tistory.com/33&quot;&gt;https://til-devsong.tistory.com/33&lt;/a&gt;&lt;/p&gt;</description>
      <category>web</category>
      <category>API</category>
      <category>http</category>
      <category>REST</category>
      <category>REST API</category>
      <category>restful api</category>
      <category>web</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/166</guid>
      <comments>https://coding-lks.tistory.com/166#entry166comment</comments>
      <pubDate>Tue, 7 Dec 2021 08:52:24 +0900</pubDate>
    </item>
    <item>
      <title>세션 / 쿠키 인증 방식</title>
      <link>https://coding-lks.tistory.com/165</link>
      <description>&lt;h3&gt;&lt;strong&gt;&lt;strong&gt;세션/쿠키 방식&lt;/strong&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;서버의 세션과 사용자 쿠키를 기반으로 하는 인증 방식입니다.&lt;/li&gt;
&lt;li&gt;서버에서 클라이언트의 세션 정보를 별도의 세션저장소에 저장하고 있습니다.(DB를 세션저장소로 많이 이용합니다.)&lt;/li&gt;
&lt;li&gt;서버가 클라이언트의 상태(로그인)를 서버에서 계속 유지(저장)하고 이 정보를 서비스에 이용하는 Stateful 서버입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;동작 방식 (순서)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;쿠키세션.png&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;397&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EVpSa/btrmMg06ntv/DYmvUiycM6ccJGlU27naX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EVpSa/btrmMg06ntv/DYmvUiycM6ccJGlU27naX0/img.png&quot; data-alt=&quot;쿠키/ 세션 기반 인증 동작 순서&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EVpSa/btrmMg06ntv/DYmvUiycM6ccJGlU27naX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEVpSa%2FbtrmMg06ntv%2FDYmvUiycM6ccJGlU27naX0%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;820&quot; height=&quot;397&quot; data-filename=&quot;쿠키세션.png&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;397&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;쿠키/ 세션 기반 인증 동작 순서&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;사용자가 로그인을 시도함.&lt;/li&gt;
&lt;li&gt;회원이 맞는지 회원 DB에서 확인합니다.(인증)&lt;/li&gt;
&lt;li&gt;사용자의 고유한 ID 값을 부여하여 세션 저장소에 저장함.&lt;/li&gt;
&lt;li&gt;그 다음 이와 연결되는 세션 ID 를 발행&lt;/li&gt;
&lt;li&gt;생성한 세션 ID를 로그인 요청에 대한 HTTP 응답 헤더에 담아 클라이언트로 전송&lt;/li&gt;
&lt;li&gt;클라이언트는 서버에서 세션 ID 를 받아 쿠키에 저장&lt;/li&gt;
&lt;li&gt;인증이 필요한 요청마다 쿠키를 HTTP 요청 헤더에 담아서 서버에 전송&lt;/li&gt;
&lt;li&gt;서버는 받은 쿠키(세션 ID)를 검증&lt;/li&gt;
&lt;li&gt;서버는 검증이 완료되면, 사용자에 맞는 데이터(유저 정보)를 내려다 줌.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;장점&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;각 사용자는 고유의 ID 값을 발급받게 되며, 서버는 쿠키 값을 받았을 때 일일이 회원 정보를 확인할 필요 없이 바로 어떤 회원인지를 알 수 있음. 덕분에 서버의 자원에 접근하기 용이함.&lt;/li&gt;
&lt;li&gt;이 방식은 기본적으로 쿠키를 매개로 인증을 거침. 쿠키는 세션 저장소에 담긴 유저 정보를 얻기 위한 열쇠일 뿐, 쿠키 자체 (세션 ID) 는 유의미한 값을 갖고 있지 않아 안전합니다.&lt;/li&gt;
&lt;li&gt;⇒ 따라서 요청이 도중에 노출되더라도 중요 정보는 서버 세션에 있기 때문에 위 방식보다는 비교적 안전함.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;단점&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;서버의 세션 저장소를 사용하기 때문에 서버 과부하가 생길 수 있음.&lt;/li&gt;
&lt;li&gt;세션 하이제킹 공격에 취얍합니다.&lt;br&gt;⇒ 쿠키를 가로챈 해커가 훔친 쿠키를 이용해 HTTP 요청을 보내면 서버의 세션 저장소에서는 사용자로 오인해 정보를 잘못 뿌려주게 됨.&lt;br&gt;그래서 완전히 안전한 방법은 아님. → 이를 보완하기 위해 HTTPS 를 사용하거나 세션에 유효 기간을 넣어주는 방법이 있음.&lt;/li&gt;
&lt;li&gt;서버의 확장이 어려워집니다.&lt;br&gt;⇒ 서버의 확장이란, 더 많은 트래픽을 감당하기 위하여 프로세스를 돌리거나 여러대의 서버 컴퓨터를 추가하는 것을 의미합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;참조:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@denmark-choco/Web-Authentication%EC%9D%B8%EC%A6%9D&quot;&gt;https://velog.io/@denmark-choco/Web-Authentication%EC%9D%B8%EC%A6%9D&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://doing7.tistory.com/87&quot;&gt;https://doing7.tistory.com/87&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://brownbears.tistory.com/439&quot;&gt;https://brownbears.tistory.com/439&lt;/a&gt;&lt;/p&gt;</description>
      <category>web</category>
      <category>auth</category>
      <category>authentication</category>
      <category>cookie</category>
      <category>session</category>
      <category>세션</category>
      <category>인증</category>
      <category>쿠키</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/165</guid>
      <comments>https://coding-lks.tistory.com/165#entry165comment</comments>
      <pubDate>Thu, 2 Dec 2021 07:09:17 +0900</pubDate>
    </item>
    <item>
      <title>[git] git merge</title>
      <link>https://coding-lks.tistory.com/164</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;git merge&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;나뉘어진 브랜치를 다시 하나의 브랜치로 합치는 것을 말한다.가장 오류가 많이 발생하는 과정이니 주의해야 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4z0nq/btqZUXb8lPo/q8zSyd0upDCBYCkyCf5Eh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4z0nq/btqZUXb8lPo/q8zSyd0upDCBYCkyCf5Eh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4z0nq/btqZUXb8lPo/q8zSyd0upDCBYCkyCf5Eh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4z0nq%2FbtqZUXb8lPo%2Fq8zSyd0upDCBYCkyCf5Eh1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;merge의 기본적인 사용방법&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;현재 브랜치를 메인이 되는 브랜치로 전환합니다.&lt;/li&gt;
&lt;li&gt;수정된 브랜치를 Merge해야 오류가 발생하지 않습니다.&lt;/li&gt;
&lt;li&gt;여러 개의 브랜치들을 한꺼번에 Merge할 때도 마찬가지로 차례차례 기존 브랜치 상태에서 Merge를 진행합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1615474060325&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#merge 하는 방법 

git checkout &amp;lt;메인이되는branch&amp;gt; 
git merge &amp;lt;합쳐질branch&amp;gt;//현재 branch의 내용과 합칠 브랜치이름&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;&amp;nbsp;merge의 종류&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;머지는 두가지 종류로 나눌 수 있습니다.(아래의 그림들에서 C0가 가장 오래된 commit입니다. )&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. fast-forward&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;패스트 포워드란 뿌리가 되는 브랜치에 변화가 주어지지 않은 채 다른 변화가 생긴 브랜치로 머지를 하게 되는 것 그러면 새로운 커밋이 생성되지 않고 머지를 한 대상 브랜치의 마지막 커밋에 뿌리가 되는 브랜치가 자리잡게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ygiF7/btqZRs4ZjKe/sSjUCqeFTvZCu2BcxibaY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ygiF7/btqZRs4ZjKe/sSjUCqeFTvZCu2BcxibaY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ygiF7/btqZRs4ZjKe/sSjUCqeFTvZCu2BcxibaY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FygiF7%2FbtqZRs4ZjKe%2FsSjUCqeFTvZCu2BcxibaY0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YSWci/btqZVnuQXke/AklqFFnk7ktzePnHrvFry0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YSWci/btqZVnuQXke/AklqFFnk7ktzePnHrvFry0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YSWci/btqZVnuQXke/AklqFFnk7ktzePnHrvFry0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYSWci%2FbtqZVnuQXke%2FAklqFFnk7ktzePnHrvFry0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;서로 다른 상태를 병합하는 것이 아니고 master를 hotfix 위치로 이동만 해도 되는 상태이기 때문에 별도의 머지를 위한 커밋이 발생하지 않습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2. non-fast-forward&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;패스트 포워드가 아니므로 새로운 커밋을 생성해 그곳에 머지를 하게됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2PqJb/btqZQHg0YtV/IG4EItZKKrdGKDRya6qRcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2PqJb/btqZQHg0YtV/IG4EItZKKrdGKDRya6qRcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2PqJb/btqZQHg0YtV/IG4EItZKKrdGKDRya6qRcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2PqJb%2FbtqZQHg0YtV%2FIG4EItZKKrdGKDRya6qRcK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;iss53을 master에 병합해보려고 합니다. 아예 다른 결과물을 갖고있는 master와 iss53은 merge되어 C6이라는 commit을 만들게 됩니다.&lt;/p&gt;
&lt;p&gt;이렇게 병합할 때 새로운 commit을 생성할 경우를&amp;nbsp;&lt;b&gt;merge commit&lt;/b&gt;&amp;nbsp;이라고 합니다.&lt;/p&gt;
&lt;p&gt;-&amp;gt;위의 merge의 기본적인 사용방법이 non-fast-forward방식의 merge입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;다시 정리하자면&amp;nbsp;&lt;b&gt;merge commit&lt;/b&gt;은 서로 다른 상태의 브랜치를 병합해서 새로운 commit을 만드는 경우이고&lt;/p&gt;
&lt;p&gt;&lt;b&gt;fast forward&lt;/b&gt;는 동일 내용이 포함되는 브랜치일 경우 브랜치 이동만으로 병합해서 따로 commit을 생성하지 않는 경우입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;충돌(Conflict) 발생 &amp;rarr; merge실패&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;머지를 할 경우 충돌의 위험이 있을 수 있습니다.&lt;/p&gt;
&lt;p&gt;충돌은 다음과 같은 경우에 생기게 됩니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;각&amp;nbsp;브랜치에서 같은 파일의 이름으로 그 내용이 다른데 머지를 시도할 경우 -&amp;gt; 충돌발생!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;충돌이 발생하게 되면 index 파일에는 그 둘 브랜치 사이의 base 커밋의 파일 내용과 각 브랜치의 커밋의 서로 다른 파일의 내용을 참조해서 총 세개의 파일 내용을 참조하게 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;여기서 깃은 이 문제를 3way merge(automatic merge) 방법을 통해 이 충돌을 해결하려고 시도합니다.&lt;/p&gt;
&lt;p&gt;하지만 같은 파일인데 내용이 서로 상이하게 바뀌었을 경우는 깃에서 해결을 하지 못하고 충돌 에러를 발생시킵니다.&lt;/p&gt;
&lt;p&gt;그러면 이때 해당 충돌 기록을 살피며 해당 파일의 내용을 살펴보며 직접 수정을 해야합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;3way merge&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;서로 다른 브랜치의 커밋을 머지할 경우 발생하는 충돌에 대해서 깃은 3way merge라는 방법을 통해 충돌을 해결합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이를 조금 더 자세히 살펴보면&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;3way merge는 base의 내용을 참고한다. (base에서 해당 파일이 어떤 내용을 담고 있었는지를 파악한다.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;한 브랜치의 파일만 바뀐 경우&amp;nbsp;바뀐 쪽으로&amp;nbsp;변경이 된다. (바뀐 쪽이 더 진보된것이므로..?)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;두개 다 바뀌지 않았을 때는 base의 내용을 그대로 따른다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;이름이 같은 두 파일의 내용이 서로 다르게 바뀌었을 때는 사용자가 직접 수정을 해야 한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;참조:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://yuja-kong.tistory.com/51&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;yuja-kong.tistory.com/51&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://sublivan.tistory.com/29?category=905894&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;sublivan.tistory.com/29?category=905894&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://mafams.tistory.com/40&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;mafams.tistory.com/40&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>git(분산 버전 관리 시스템)</category>
      <category>3way merge</category>
      <category>fast forward</category>
      <category>git merge</category>
      <category>non fast forward</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/164</guid>
      <comments>https://coding-lks.tistory.com/164#entry164comment</comments>
      <pubDate>Thu, 11 Mar 2021 23:51:41 +0900</pubDate>
    </item>
    <item>
      <title>[git] git branch</title>
      <link>https://coding-lks.tistory.com/163</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;브랜치(branch)란?&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;브랜치란 독립적으로 어떤 작업을 진행하기 위한 개념입니다. 필요에 의해 만들어지는 각각의 브랜치는 다른 브랜치의 영향을 받지 않기 때문에, 여러 작업을 동시에 진행할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;rArr; 특정 커밋으로부터 분기되는 포인터.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/x8SMj/btqZOsjjy0E/PkEJsVmjnFSPRVm9wiJ7kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/x8SMj/btqZOsjjy0E/PkEJsVmjnFSPRVm9wiJ7kk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/x8SMj/btqZOsjjy0E/PkEJsVmjnFSPRVm9wiJ7kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fx8SMj%2FbtqZOsjjy0E%2FPkEJsVmjnFSPRVm9wiJ7kk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;또한 이렇게 만들어진 브랜치는 다른 브랜치와 병합(Merge)함으로써, 작업한 내용을 다시 새로운 하나의 브랜치로 모을 수 있습니다.&lt;/p&gt;
&lt;p&gt;아래 그림에서 보다시피 여러 명이서 동시에 작업을 진행할 수 있지만 다른 사람의 작업영역에 영향을 주거나 받지 않도록, master branch에서 분기된 저의 작업 전용 branch를 만듭니다.&lt;/p&gt;
&lt;p&gt;각자의 작업을 진행하고 완료되면 master branch에 자신의 branch 변경 이력을 master branch에 적용합니다.(merge)&lt;/p&gt;
&lt;p&gt;이런 방식으로 다른 사람의 작업에 영향을 받지 않고 독립적으로 할당된 작업을 수행하고 그 결과를 master branch에 병합시켜 나갑니다.&lt;/p&gt;
&lt;p&gt;이런 방식의 작업의 장점은 작업단위(branch)로 변경 이력이 남게 되므로 문제가 생겼을 때 원인을 발견하고 해결 하거나 그에 따른 대책을 세우기 용이해진다는 점이 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dR2cRi/btqZPHAx06W/Dz2ygRoiRzA9Rt1kJD3jgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dR2cRi/btqZPHAx06W/Dz2ygRoiRzA9Rt1kJD3jgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dR2cRi/btqZPHAx06W/Dz2ygRoiRzA9Rt1kJD3jgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdR2cRi%2FbtqZPHAx06W%2FDz2ygRoiRzA9Rt1kJD3jgk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;브랜치 만들기&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;'delveop' 이라는 이름으로 새로운 브랜치를 작성합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;브랜치는 branch 란 명령어로 만들 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1615380303911&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git branch [branchname]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;'&lt;span style=&quot;color: #333333;&quot;&gt;delveop&lt;/span&gt;' 이라는 이름으로 브랜치를 만들어 봅시다.&lt;/p&gt;
&lt;pre id=&quot;code_1615380298887&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$git branch delveop&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;옵션을 지정하지 않고 branch 명령어를 실행하면 브랜치 목록 전체를 확인할 수 있습니다. 앞 부분에 * 이 붙어있는 것이 현재 선택된 브랜치입니다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이 시점까지의 이력을 보면 다음과 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1615380292520&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$git branch 
  delveop 
* master&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이외에&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1615380417839&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//브랜치 조회
git branch

//브랜치생성
git branch [branchname]

//브랜치 수정
git branch -m [branchname] [newname]

//브랜치 삭제
git branch -d [branchname]

//브랜치 마다 마지막 커밋내용 보기
git branch -v&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;등의 명령어들이 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;참조:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://sublivan.tistory.com/27?category=905894&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;sublivan.tistory.com/27?category=905894&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://backlog.com/git-tutorial/kr/stepup/stepup1_1.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;backlog.com/git-tutorial/kr/stepup/stepup1_1.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://medium.com/@js230023/git-%EB%B8%8C%EB%9E%9C%EC%B9%98-branch-%EB%9E%80-eabbbc570bf5&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;medium.com/@js230023/git-%EB%B8%8C%EB%9E%9C%EC%B9%98-branch-%EB%9E%80-eabbbc570bf5&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>git(분산 버전 관리 시스템)</category>
      <category>git branch</category>
      <category>협업</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/163</guid>
      <comments>https://coding-lks.tistory.com/163#entry163comment</comments>
      <pubDate>Wed, 10 Mar 2021 21:50:24 +0900</pubDate>
    </item>
    <item>
      <title>[git] git snapshot</title>
      <link>https://coding-lks.tistory.com/162</link>
      <description>&lt;h3&gt;snapshot&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;특정 시점에서 파일, 폴더 또는 워크스페이스의 상태&lt;/b&gt;를 의미합니다.&lt;/p&gt;
&lt;p&gt;스냅샷을 통해 특정 시점에 어떤 파일에 어떤 내용이 기록되어 있었는지, 폴더 구조는 어떠했는지, 어떤 파일이 존재했는지 등 저장소의 모든 정보를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;Git에서는 새로운 버전을 기록하기 위한 명령인 커밋(commit)을 실행하면 스냅샷이 저장된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;GIT&lt;b&gt;은 스냅샷으로 관리됩니다.&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;기존 버전관리 시스템(서브버전, 퍼포스 등)과 깃과의 큰 차이는 버전 데이터를 관리하는 방식입니다.&lt;/p&gt;
&lt;p&gt;다른 시스템은 주로 파일을 중심으로 파일이 변경된 내역을 관리하고 있죠.&lt;/p&gt;
&lt;p&gt;데이터를 보는 관점이 어떤 파일이 존재하고 각 파일이 시간에따라 변경된 차이점에 있게 됩니다.&lt;/p&gt;
&lt;p&gt;이를 델타 베이스드 버전 관리(delta-based version control)이라고 하죠.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oahDW/btqZQHmvGnI/v05sEJ1Ar0lL6qAytnUrZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oahDW/btqZQHmvGnI/v05sEJ1Ar0lL6qAytnUrZk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oahDW/btqZQHmvGnI/v05sEJ1Ar0lL6qAytnUrZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoahDW%2FbtqZQHmvGnI%2Fv05sEJ1Ar0lL6qAytnUrZk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;프로젝트 처음에 File A, File B, File C로 Vesrion 1을 시작했습니다.&lt;/p&gt;
&lt;p&gt;그리고 File A와 File C에 기능을 구현해서 Version 2가 되었습니다.&lt;/p&gt;
&lt;p&gt;이때 File A와 File C는 Version 1 대비 변경된 내용이 delta 1으로 저장이 됩니다.&lt;/p&gt;
&lt;p&gt;Version 2의 코드를 받아 보려고 하면 원본인 File A와 File C에 변경 사항인 각각의 delta 1을 적용해서 받아오는 겁니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sW58I/btqZKBnN9mQ/3wk9nBR0rIYH8fY0dRYoI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sW58I/btqZKBnN9mQ/3wk9nBR0rIYH8fY0dRYoI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sW58I/btqZKBnN9mQ/3wk9nBR0rIYH8fY0dRYoI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsW58I%2FbtqZKBnN9mQ%2F3wk9nBR0rIYH8fY0dRYoI1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;이와는 달리 깃은 아주 다르게 데이터가 관리됩니다.&lt;/p&gt;
&lt;p&gt;데이터를 바라보는 관점이 완전히 다른데요.&lt;/p&gt;
&lt;p&gt;파일을 하나씩 별도로 보면서 차이점만 따로 관리하지 않고 프로젝트 전체를 스냅샷 형태로 관리합니다.&lt;/p&gt;
&lt;p&gt;마치 매 순간마다 프로젝트 전체를 사진으로 찍어서 관리하는 것과 같은데요.&lt;/p&gt;
&lt;p&gt;Version 1에서 Version 2로 변경이 되면 깃은 Version 2의 스냅샷을 하나 만들어 놓고 그 스냅샷을 가르키는 레퍼런스를 저장합니다.&lt;/p&gt;
&lt;p&gt;효율을 위해서 변경되지 않은 File B는 다시 새로 저장을 하는 것이 아니라 이미 저장되어 있는 파일에 링크만 걸어 줍니다.&lt;/p&gt;
&lt;p&gt;버전이 변경되는 과정을 보면 마치 연속된 스냅 사진을 보는 것 같습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;Git이 작고 빠를 수 있는 이유?&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;Git에서 커밋을 하면 워크스페이스 전체가 스냅샷으로 저장되지만 덩치가 배로 불어나지는 않습니다.&lt;/p&gt;
&lt;p&gt;Git은 마지막 커밋의 스냅샷만 통째로 저장하고 나머지 커밋에 대해서는 스냅샷과 스냅샷이 차이를 기록한 &amp;lsquo;델타&amp;rsquo;를 저장하기 때문입니다.&lt;/p&gt;
&lt;p&gt;Git의 스냅샷과 델타 구성&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JilgG/btqZPHUOzQU/VoVW5EW6VLT2COGeRQgEw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JilgG/btqZPHUOzQU/VoVW5EW6VLT2COGeRQgEw1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JilgG/btqZPHUOzQU/VoVW5EW6VLT2COGeRQgEw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJilgG%2FbtqZPHUOzQU%2FVoVW5EW6VLT2COGeRQgEw1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;이렇게 하면 버전별로 전체 스냅샷을 모두 저장하지 않아도 마지막 스냅샷을 기준으로 특정 시점의 스냅샷을 만들 수 있습니다.&lt;/p&gt;
&lt;p&gt;저장소의 크기도 자연히 줄어듭니다. (Git을 개발한 측에서는 VCS 중에서 저장소 크기가 가장 작다고 주장한다&lt;/p&gt;
&lt;p&gt;솔직히 아직도 File delta 기반 버젼 관리 vs snapshop 기반 버젼 관리의 차이가 확실히 이해가 되질 않았다.&lt;/p&gt;
&lt;p&gt;하지만 아래의 이미지를 보면 좀 더 이해가 될 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;File Delta Version Tracking&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKFTpt/btqZMm4KIIn/2gurtwzR6FDQ4ckLMmj3i0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKFTpt/btqZMm4KIIn/2gurtwzR6FDQ4ckLMmj3i0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKFTpt/btqZMm4KIIn/2gurtwzR6FDQ4ckLMmj3i0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKFTpt%2FbtqZMm4KIIn%2F2gurtwzR6FDQ4ckLMmj3i0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3eEzy/btqZLtpx9X8/EKeRY1XEWi4gE7ZImoNoHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3eEzy/btqZLtpx9X8/EKeRY1XEWi4gE7ZImoNoHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3eEzy/btqZLtpx9X8/EKeRY1XEWi4gE7ZImoNoHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3eEzy%2FbtqZLtpx9X8%2FEKeRY1XEWi4gE7ZImoNoHk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOBig9/btqZPG9tz2j/sAvKSBds2fe5AFOQ4XnZZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOBig9/btqZPG9tz2j/sAvKSBds2fe5AFOQ4XnZZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOBig9/btqZPG9tz2j/sAvKSBds2fe5AFOQ4XnZZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOBig9%2FbtqZPG9tz2j%2FsAvKSBds2fe5AFOQ4XnZZK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vCJAd/btqZMoO0ozM/qW1mcEcxwH7IEcBFzZXcek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vCJAd/btqZMoO0ozM/qW1mcEcxwH7IEcBFzZXcek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vCJAd/btqZMoO0ozM/qW1mcEcxwH7IEcBFzZXcek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvCJAd%2FbtqZMoO0ozM%2FqW1mcEcxwH7IEcBFzZXcek%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Snapshot Version Tracking&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r1nym/btqZMnWR80a/Bqggxrf7r5IVjvKKKNrvP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r1nym/btqZMnWR80a/Bqggxrf7r5IVjvKKKNrvP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r1nym/btqZMnWR80a/Bqggxrf7r5IVjvKKKNrvP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr1nym%2FbtqZMnWR80a%2FBqggxrf7r5IVjvKKKNrvP1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;&amp;nbsp;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SbXYz/btqZPGPaORN/NEZYk6zPHC3akCigZxzCT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SbXYz/btqZPGPaORN/NEZYk6zPHC3akCigZxzCT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SbXYz/btqZPGPaORN/NEZYk6zPHC3akCigZxzCT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSbXYz%2FbtqZPGPaORN%2FNEZYk6zPHC3akCigZxzCT0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;bull; add&amp;nbsp;means add file contents to the snapshot &amp;bull; The SHA hash is a pointer into a key-value store.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xXUTK/btqZRr4Mkpy/6yHcib8bffXCaIhAkpmpg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xXUTK/btqZRr4Mkpy/6yHcib8bffXCaIhAkpmpg0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xXUTK/btqZRr4Mkpy/6yHcib8bffXCaIhAkpmpg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxXUTK%2FbtqZRr4Mkpy%2F6yHcib8bffXCaIhAkpmpg0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;bull; There is no history unless you walk through the manifests.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvo1Il/btqZPGVTIxH/qBjpFEXvT3IoVkWOh2Bd70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvo1Il/btqZPGVTIxH/qBjpFEXvT3IoVkWOh2Bd70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvo1Il/btqZPGVTIxH/qBjpFEXvT3IoVkWOh2Bd70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbvo1Il%2FbtqZPGVTIxH%2FqBjpFEXvT3IoVkWOh2Bd70%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;bull; A rename just changes the manifest pointer.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;git은 snapshot based 버전 관리 시스템이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;delta bases 버젼 관리 시스템에서 만약 하나의 파일의 거의 대부분을 수정할 일이 생겼다면 delta의 용량도 무시하지 못하게 커질 것이다. 또 version2던 version3던 첫번째 파일에서 시작해 변경점을 적용하면서 접근하기 때문에 제일 최신의 버젼에 접근할 때 속도면에서 손해가 있을 것이다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;하지만 Git은 마지막 커밋의 스냅샷만 통째로 저장하고 나머지 커밋에 대해서는 스냅샷과 스냅샷이 차이를 기록한 &amp;lsquo;델타&amp;rsquo;를 저장하기 때문입니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;또 마지막 커밋에서 시작하기 때문에 최근의 커밋에 접근할 때에 자원면에서 손해가 덜 할 것이다...라고 이해했습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;참조:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://courses.csail.mit.edu/6.S194/13/lessons/03-git/vcs.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;courses.csail.mit.edu/6.S194/13/lessons/03-git/vcs.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://tech.10000lab.xyz/git/how-git-is-different.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;tech.10000lab.xyz/git/how-git-is-different.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://thebook.io/080212/ch04/04/02/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;thebook.io/080212/ch04/04/02/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;잘못된 내용 찝어 주시는 건 언제나 환영입니다!^^&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>git(분산 버전 관리 시스템)</category>
      <category>delta based</category>
      <category>Git</category>
      <category>snapshot based</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/162</guid>
      <comments>https://coding-lks.tistory.com/162#entry162comment</comments>
      <pubDate>Wed, 10 Mar 2021 21:26:41 +0900</pubDate>
    </item>
    <item>
      <title>[git] git clone</title>
      <link>https://coding-lks.tistory.com/160</link>
      <description>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;git clone&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;누군가의 변경 이력이 적용된 원격 저장소가 있으면, 그걸 웹에서 통째로 복제해와 내 PC에서 직접 작업할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;-&amp;gt; 원격저장소와 똑같은 로컬 저장소를 내 PC에 만듭니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1615300533203&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git clone &amp;lt;원격저장소 주소&amp;gt;

//b : 원격저장소의 특정 브랜치 내용을 clone
git clone -b [브랜치이름] [원격저장소주소]

//-b == --branch&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;원격 저장소를 복제하려면, 클론(Clone)이라는 조작을 수행합니다.&lt;/p&gt;
&lt;p&gt;복제란 원격 저장소의 내용을 통째로 다운로드하는 것을 말합니다. 복제한 저장소를 다른 PC에서 로컬 저장소로 사용할 수 있게 됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;commit history&lt;span style=&quot;color: #333333;&quot;&gt;도 함께 로컬 저장소에 복제되어 오므로, 원래 원격 저장소와 똑같이 이력을 참조하고 커밋을 진행할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;참조 :&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://sublivan.tistory.com/26?category=905894&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;sublivan.tistory.com/26?category=905894&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://backlog.com/git-tutorial/kr/intro/intro3_2.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;backlog.com/git-tutorial/kr/intro/intro3_2.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>git(분산 버전 관리 시스템)</category>
      <category>git clone</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/160</guid>
      <comments>https://coding-lks.tistory.com/160#entry160comment</comments>
      <pubDate>Tue, 9 Mar 2021 23:38:10 +0900</pubDate>
    </item>
    <item>
      <title>[git] git pull</title>
      <link>https://coding-lks.tistory.com/159</link>
      <description>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;git pull&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;git pull은 다른 사람이 PR을 통해서 코드를 업데이트했거나, Github를 통해서 commit했을 때(Github를 통해서도 간단한 commit을 할 수 있습니다) 그 내용을 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;로컬 저장소로 내려받는 명령어&lt;/span&gt;입니다&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;-&amp;gt; &lt;span&gt;원격 저장소의 작업 내역이 나의 로컬 저장소의 작업내역과 다를 때는 내 변경사항을 원격저장소에 업로드(Push)하면 충돌이 발생합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span&gt;이럴때 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;로컬 저장소를 원격저장소의 최신 변경사항으로 맞추어야 내 로컬 변경사항을 업로드할 수 있습니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span&gt;이러한 용도로 많이 사용됩니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1615215382812&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git push &amp;lt;저장소명&amp;gt; &amp;lt;브랜치명&amp;gt;

$ git pull origin master&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;비밀번호를 파일로 저장하기&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;it pull을 할 때는 깃허브의 유저이름과 비밀번호를 쳐야하는 경우가 많습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1615215473996&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git config --global credential.helper 'store --file 경로'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;명령어를 사용하면 해당 경로에 비밀번호를 저장한 파일이 생성됩니다.&lt;/p&gt;
&lt;p&gt;* 파일로 저장된 만큼 보안에 취약할 수 있습니다. *&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;참조:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://uang.tistory.com/11&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;uang.tistory.com/11&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.zerocho.com/category/Git/post/581042fdcae2d100152ceae6&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.zerocho.com/category/Git/post/581042fdcae2d100152ceae6&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://sublivan.tistory.com/25?category=905894&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;sublivan.tistory.com/25?category=905894&lt;/a&gt;&lt;/p&gt;</description>
      <category>git(분산 버전 관리 시스템)</category>
      <category>git pull</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/159</guid>
      <comments>https://coding-lks.tistory.com/159#entry159comment</comments>
      <pubDate>Mon, 8 Mar 2021 23:52:46 +0900</pubDate>
    </item>
    <item>
      <title>[git] git push</title>
      <link>https://coding-lks.tistory.com/158</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;git push&lt;/h3&gt;
&lt;p&gt;git push&lt;span&gt;는 원격 저장소(remote repository)에 코드 변경분을 업로드하기 위해서 사용하는 Git 명령어 입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;git push&lt;span&gt;를 해줘야, 그 동안 로컬 저장소에서 남겨놓은 코드 변경 이력들이 원격 저장소로 전송이 됩니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;기본-사용법&quot; data-ke-size=&quot;size23&quot;&gt;기본 사용법&lt;/h3&gt;
&lt;p&gt;git push&lt;span&gt;&amp;nbsp;&lt;/span&gt;명령어는 기본적으로 원격 저장소명과 브랜치명을 인자로 받습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1615018582586&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git push &amp;lt;저장소명&amp;gt; &amp;lt;브랜치명&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;예를 들어,&lt;span&gt;&amp;nbsp;&lt;/span&gt;my-feature라는 브랜치에 남겨놓은 코드 변경 이력을&lt;span&gt;&amp;nbsp;&lt;/span&gt;origin라는 원격 저장소에 올리기 위한&lt;span&gt;&amp;nbsp;&lt;/span&gt;git push&lt;span&gt;&amp;nbsp;&lt;/span&gt;명령어는 다음과 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1615018587786&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git push origin my-feature&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;원격 저장소명은&lt;span&gt;&amp;nbsp;&lt;/span&gt;git clone을 통해 저장소를 복제를 했다면 일반적으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;origin이며&lt;span&gt;&amp;nbsp;&lt;/span&gt;git remote&lt;span&gt;&amp;nbsp;&lt;/span&gt;명령어를 통해서 정확한 저장소명을 알아낼 수도 있습니다.&lt;/p&gt;
&lt;p&gt;$ &lt;span&gt;git&lt;/span&gt; remote origin&lt;/p&gt;
&lt;pre id=&quot;code_1615018591354&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git remote origin&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;-u옵션으로 저장소 이름과 브랜치 이름 생략&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;-u 옵션을 붙이면, 대상의 브랜치를 원격 저장소에 추적시킬 수 있습니다. 이로 인해, 이후의 push 또는 fetch / pull 명령어로 저장소를 생략한 경우에도 정확하게 변경내용을 반영/취득하는 것이 가능해 집니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;예를 들어, 다음과 같이 저장소명과 브랜치명을 넘기면서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;-u&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;옵션과 함께&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;git push&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;명령어를 날리면,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1615018857981&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git push -u origin master

git commit -m &quot;change1&quot;

git push&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그 이후 커밋한 코드 변경분을 git push 명령어만으로 원격저장소에 올릴 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;브랜치를 전환하면서 작업할 경우&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;여러 브랜치를 넘나 들면서 작업을 하는 경우에는 브랜치를 넘나 들때마다 최초에 한 번 인자를 넘겨야 합니다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span style=&quot;color: #000000;&quot;&gt; 대부분의 경우에는 로컬 저장소와 원격 저장소에서 동일한 브랜치 이름을 사용하기 때문에 항상 현재 브랜치를 기준으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;git push&lt;span&gt;&amp;nbsp;&lt;/span&gt;명령어가 작동한다면 매우 편리할 것 같습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이를 위해서는 약간의 설정이 필요한데요. 다음과 같이&lt;span&gt;&amp;nbsp;&lt;/span&gt;push.default&lt;span&gt;&amp;nbsp;&lt;/span&gt;설정을&lt;span&gt;&amp;nbsp;&lt;/span&gt;current로 설정해줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1615019104272&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git config --global push.default current&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;설정 후 부터는 어느 브랜치에서 작업을 하든&lt;span&gt;&amp;nbsp;&lt;/span&gt;git push만 날리면 원격 저장소에 동일한 브랜치로 코드 변경분이 업로드됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1615019108129&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git push&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;참조 :&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;a href=&quot;https://www.daleseo.com/git-push/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.daleseo.com/git-push/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;a href=&quot;https://backlog.com/git-tutorial/kr/reference/remote.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;backlog.com/git-tutorial/kr/reference/remote.html&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;a href=&quot;https://sublivan.tistory.com/24?category=905894&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;sublivan.tistory.com/24?category=905894&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>git(분산 버전 관리 시스템)</category>
      <category>git push</category>
      <category>push.default</category>
      <category>u</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/158</guid>
      <comments>https://coding-lks.tistory.com/158#entry158comment</comments>
      <pubDate>Sat, 6 Mar 2021 17:28:41 +0900</pubDate>
    </item>
    <item>
      <title>[git] git remote</title>
      <link>https://coding-lks.tistory.com/157</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;git remote&lt;span&gt;명령은 프로젝트의 리모트 저장소를 관리하는 명령어입니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;git-remote&quot; data-ke-size=&quot;size23&quot;&gt;git remote&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등록된 리모트 저장소 이름만 보여줍니다.&lt;/p&gt;
&lt;pre id=&quot;code_1615014332017&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git remote 
origin&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;git-remote--v&quot; data-ke-size=&quot;size23&quot;&gt;git remote -v&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등록된 저장소 이름과 URL을 표시합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1615014205499&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git remote -v 
origin  https://github.com/Lks9172/toy42.git (fetch) 
origin  https://github.com/Lks9172/toy42.git (push)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;git-remote-add-리모트이름-경로&quot; data-ke-size=&quot;size23&quot;&gt;git remote add (리모트이름) (&lt;span&gt;원격저장소 주소&lt;/span&gt;)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새 리모트를 추가합니다. (경로)영역에는 URL이나 파일경로를 넣을수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1615014240563&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git remote add mylotto https://github.com/Lks9172/mylotto.git 
$ git remote add origin https://github.com/Lks9172/toy42.git
$ git remote -v 
mylotto https://github.com/Lks9172/mylotto.git (fetch) 
mylotto https://github.com/Lks9172/mylotto.git (push) 
origin  https://github.com/Lks9172/toy42.git (fetch) 
origin  https://github.com/Lks9172/toy42.git (push)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;git-remote-show-리모트이름&quot; data-ke-size=&quot;size23&quot;&gt;git remote show (리모트이름)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 리모트 경로의 branch와 정보를 표시합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1615014299835&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git remote show origin
* remote origin
  Fetch URL: https://github.com/Lks9172/toy42.git
  Push  URL: https://github.com/Lks9172/toy42.git
  HEAD branch: master
  Remote branches:
    develop tracked
    master  tracked
  Local branch configured for 'git pull':
    master merges with remote master
  Local refs configured for 'git push':
    develop pushes to develop (up to date)
    master  pushes to master  (up to date)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;git-remote-rm-리모트이름&quot; data-ke-size=&quot;size23&quot;&gt;git remote rm (리모트이름)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리모트 경로를 제거합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1615014317890&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git remote -v
mylotto https://github.com/Lks9172/mylotto.git (fetch) 
mylotto https://github.com/Lks9172/mylotto.git (push) 
origin  https://github.com/Lks9172/toy42.git (fetch) 
origin  https://github.com/Lks9172/toy42.git (push)

// $ git remote remove mylotto도 같은 동작을 합니다.
$ git remote rm mylotto

$ git remote -v
origin  https://github.com/Lks9172/toy42.git (fetch) 
origin  https://github.com/Lks9172/toy42.git (push)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;git remote &lt;span style=&quot;color: #333333;&quot;&gt;set-url (리모트이름) (원격저장소 경로)&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 리모트이름의 원격저장소 경로를 변경합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1615014714433&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git remote -v
 	# View existing remotes
origin  https://github.com/Lks9172/toy42.git (fetch) 
origin  https://github.com/Lks9172/toy42.git (push)

$ git remote set-url origin https://github.com/Lks9172/mylotto.git
# Change the 'origin' remote's URL

$ git remote -v
# Verify new remote URL
origin  https://github.com/Lks9172/mylotto.git (fetch)
origin  https://github.com/Lks9172/mylotto.git (push)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;git remote rename &lt;span style=&quot;color: #333333;&quot;&gt;(현재 리모트&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이름) (바뀔&lt;span&gt; &lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;리모트&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이름)&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;원격-저장소-이름-변경하기&quot; 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;pre id=&quot;code_1615015075080&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git remote -v
# View existing remotes
origin  https://github.com/Lks9172/toy42.git (fetch) 
origin  https://github.com/Lks9172/toy42.git (push)

$ git remote rename origin destination
# Change remote name from 'origin' to 'destination'

$ git remote -v
# Verify remote's new name
destination  https://github.com/Lks9172/mylotto.git (fetch)
destination  https://github.com/Lks9172/mylotto.git (push)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참조:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://sublivan.tistory.com/23?category=905894&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;sublivan.tistory.com/23?category=905894&lt;/a&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;a href=&quot;http://minsone.github.io/git/github-managing-remotes-changing-a-remotes-url&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;minsone.github.io/git/github-managing-remotes-changing-a-remotes-url&lt;/a&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;a href=&quot;http://minsone.github.io/git/github-managing-remotes-renaming-a-remote&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;minsone.github.io/git/github-managing-remotes-renaming-a-remote&lt;/a&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;a href=&quot;https://www.delftstack.com/ko/howto/git/how-to-remove-a-git-remote-url/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.delftstack.com/ko/howto/git/how-to-remove-a-git-remote-url/&lt;/a&gt;&lt;/p&gt;</description>
      <category>git(분산 버전 관리 시스템)</category>
      <category>git remote</category>
      <category>remote</category>
      <category>set-url</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/157</guid>
      <comments>https://coding-lks.tistory.com/157#entry157comment</comments>
      <pubDate>Sat, 6 Mar 2021 16:20:06 +0900</pubDate>
    </item>
    <item>
      <title>[git] git commit</title>
      <link>https://coding-lks.tistory.com/156</link>
      <description>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;git commit&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;준비영역(Staging Area)의 파일을 commit을 수행해 해당 git 저장소에 저장한다.&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;그리고 &lt;span style=&quot;color: #333333;&quot;&gt;준비영역(Staging Area)&lt;/span&gt;에 넘어온 변경 내용을 토대로 변경이력을 남깁니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;변경이력을 토대로 나중에 버그를 추적하거나 변경 이력을 롤백(roll back)할 때도 용이할 수 있습니다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1615010038282&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 에디터가 출력되고, 에디터에서 커밋 메시지 입력 후 저장하면 커밋됨
git commit

// 커밋 메시지를 입력후 커밋
// Staging Area에 들어간 파일에 대해서만 (워킹 디렉터리는 적용 X)
git commit -m &quot;커밋 메시지&quot;

// -a 옵션을 사용하면 
// 스테이징 절차(add)를 생략하고 바로 add와 commit을 동시에 수행한다.
git commit -a -m &quot;커밋 메시지&quot;

//마지막 커밋메시지 수정, commit내용 잘 못했을 때 수정하기
git commit --amend -m &quot;커밋 메시지&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;span&gt;git commit 취소하기&lt;/span&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;완료한 commit을 취소해야 할때가 있다.&lt;/span&gt;
&lt;ol&gt;
&lt;li&gt;&lt;span&gt;너무 일찍 commit 한경우&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;어떤 파일을 빼먹고 commit한 경우 이때, git reset&lt;span&gt;&amp;nbsp;&lt;/span&gt;HEAD^ 명령어를 통해 git commit을 취소 할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1615010528183&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//commit 목록 확인 
git log

// 기본 옵션 
// git reset &amp;lt;옵션&amp;gt; &amp;lt;돌아가고싶은 커밋&amp;gt;
git reset HEAD^ 또는 [commit 번호] 

//위와 동일 
// HEAD^는 헤드의 직전 위치를 의미한다. 즉, 현재 브랜치의 마지막 커밋을 뜻한다.

// [방법 1] HEAD를 옮겨 commit을 취소하고 해당 파일들은 unstaged 상태로 워킹 디렉터리에 보존 
git reset --mixed HEAD^ 

// [방법 2] HEAD를 옮겨 commit을 취소하고 해당 파일들은 staged 상태로 워킹 디렉터리에 보존 
// 파일 내용은 그대로 유지하면서 staging area에 올림(add 상태) 
git reset --soft HEAD^ 

// [방법 3] 저장된 커밋들 중 하나로 헤드를 이동하는 작업 
// git이 관리하는 모든 파일이 해당 커밋의 상태로 돌아갑니다.
git reset --hard HEAD^ 


git reset HEAD~2 // 마지막 2개의 commit을 취소&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;reset에 대한 더 자세한 내용은 아래 링크를 통해 알 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://git-scm.com/book/ko/v2/Git-%EB%8F%84%EA%B5%AC-Reset-%EB%AA%85%ED%99%95%ED%9E%88-%EC%95%8C%EA%B3%A0-%EA%B0%80%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;git-scm.com/book/ko/v2/Git-%EB%8F%84%EA%B5%AC-Reset-%EB%AA%85%ED%99%95%ED%9E%88-%EC%95%8C%EA%B3%A0-%EA%B0%80%EA%B8%B0&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;참조:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://sublivan.tistory.com/22?category=905894&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;sublivan.tistory.com/22?category=905894&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://gmlwjd9405.github.io/2018/05/25/git-add-cancle.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;gmlwjd9405.github.io/2018/05/25/git-add-cancle.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.daleseo.com/git-add/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.daleseo.com/git-add/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>git(분산 버전 관리 시스템)</category>
      <category>git commit</category>
      <category>HEAD^</category>
      <category>RESET</category>
      <category>준비영역(Staging Area)</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/156</guid>
      <comments>https://coding-lks.tistory.com/156#entry156comment</comments>
      <pubDate>Sat, 6 Mar 2021 15:23:15 +0900</pubDate>
    </item>
    <item>
      <title>[git] git add와 staging Area</title>
      <link>https://coding-lks.tistory.com/155</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;git add&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;git add는 &lt;span style=&quot;color: #333333;&quot;&gt;working directory&lt;/span&gt;(작업 디렉토리) 상의 변경 내용을 &lt;span style=&quot;color: #333333;&quot;&gt;staging area&lt;/span&gt;(스테이징 영역)에 추가하기 위해서 사용하는 Git 명령어입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1615002871197&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git add [파일이름 또는 디렉토리 이름]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;staging-area&quot; data-ke-size=&quot;size23&quot;&gt;Staging Area(스테이징 영역)&lt;/h3&gt;
&lt;p&gt;git add&lt;span&gt;&amp;nbsp;&lt;/span&gt;명령어를 사용할 때 반드시 숙지해야하는 개념이 바로 Staging Area(스테이징 영역)입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;스테이징 영역은 작업 디렉토리와 Git 저장소의 변경 이력 사이에 징검다리 역할을 합니다.&lt;/p&gt;
&lt;p&gt;작업 디렉토리는 아직 커밋할 준비가 안된 변경 내용을 자유롭게 수정할 수 있는 공간인 반면에, &lt;span style=&quot;background-color: #9feec3;&quot;&gt;스테이징 영역은 커밋할 준비가 된 변경 내용이 Git 저장소에 기록되기 전에 대기하는 장소&lt;/span&gt;라고 생각할 수 있습니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;git add&lt;span&gt;&amp;nbsp;&lt;/span&gt;명령어를 사용하면 현재 작업 디렉토리에 있는 모든 또는 일부 변경 내용을 스테이징 영역으로 이동시킬 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;참조:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://m.blog.naver.com/bluesky4381/221208245150&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;m.blog.naver.com/bluesky4381/221208245150&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.daleseo.com/git-add/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.daleseo.com/git-add/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>git(분산 버전 관리 시스템)</category>
      <category>git add</category>
      <category>staging area</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/155</guid>
      <comments>https://coding-lks.tistory.com/155#entry155comment</comments>
      <pubDate>Sat, 6 Mar 2021 13:01:57 +0900</pubDate>
    </item>
    <item>
      <title>[git] 파일 상태 정리</title>
      <link>https://coding-lks.tistory.com/154</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;Git 프로젝트에서 파일의 여러가지 상태&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span&gt;Git은 파일들의 버전을 관리하는 버전 관리 도구인 만큼 각각의 파일들이 어떤 상태인지를 여러가지로 분류하여 관리 합니다. 이러한 상태들을 이해하는 것은 어렵고, 세 가지 영역과 추적 여부, 수정 여부에 따라 복합적으로 여러가지 상태를 가지기 때문에 각각을 확실히 이해하는것이 중요합니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래에서 Git의 상태와 예시를 들어 이해를 돕고 git status명령어를 이용해 현재 파일의 상태를 확인해보겠습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;Untracked와 Tracked 상태&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span&gt;워킹 디렉터리에 있는 여러가지 파일들은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;&lt;span&gt;Git의 추적 관리 여부에 따라 각각 크게 두 가지 상태&lt;/span&gt;&lt;/b&gt;&lt;span&gt;로 나눌 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Untracked와 Tracked 상태인데 뜻 그대로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Tracked&lt;/span&gt;&lt;span&gt;는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Git이 해당 파일을 추적 및 관리하는 상태&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;이며,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;Untracked&lt;/span&gt;&lt;span&gt;는 반대로 아직 Git이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;해당 파일을 추적 및 관리하지 않는 상태&lt;/span&gt;&lt;span&gt;를 말합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;이는 워킹 디렉터리에 존재하는 파일이라고 해서 모두 Git이 관리하는 파일은 아니라는 뜻이기도 합니다. 따라서 Untracked 파일은 수정이 되거나 삭제가 되어도 Git은 전혀 신경쓰지 않으며, 해당 파일을 어떤 이유로 잃어버려도 복구가 불가능합니다. 대신 이러한 추적관리 시스템을 이용하면 Tracked 상태가 아닌 불필요한 파일들을 커밋하게 되는 실수를 방지할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Tracked 상태인 파일은 최소한 한번은 git add 명령을 통해 Staging Area에 포함되거나, Commit을 통해 Git 디렉터리에 저장된 파일입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;정리&lt;/span&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;span&gt;워킹 디렉터리에 있지만 git add나 commit 하지 않은 파일은 Untracked 상태, git add나 commit 했던 적이 있는 파일들은 Tracked 상태입니다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;span&gt;Tracked 상태인 파일들은 Git이 저장 및 관리하며, Untracked 파일은 Git이 신경쓰지 않습니다.&lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;.gitignore파일을 생성해 untracked한 파일을 지정할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Git 3가지 상태&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CA3J8/btqZpgpA3ba/gjsGMnk5J8C3mF45YeqssK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CA3J8/btqZpgpA3ba/gjsGMnk5J8C3mF45YeqssK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CA3J8/btqZpgpA3ba/gjsGMnk5J8C3mF45YeqssK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCA3J8%2FbtqZpgpA3ba%2FgjsGMnk5J8C3mF45YeqssK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span&gt;Unmodified와 Modified 상태&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Untracked와 Tracked가 추적 관리 여부 관점에서 바라본 상태 였다면, 파일의 변경 여부에 따라&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;&lt;span&gt;Modified(변경 발생)&lt;/span&gt;&lt;/b&gt;&lt;span&gt;과&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;&lt;span&gt;Unmodifed(변경 없음)&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;상태로 나눌 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;변경 발생 기준은 '파일이 Staged 또는 Commit된 시점 이후로 변경 되었는가' 입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;Staged&lt;/span&gt;는 git add 명령어를 통해 Staging Area에 내역을 추가한 상태입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;이 상태에서 git commit 명령을 통해한 Commit한 경우 &lt;span style=&quot;color: #000000; background-color: #9feec3;&quot;&gt;Committed 상태&lt;/span&gt;입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Git이 관리하지 않는 상태인 Untracked 파일은 두 가지 변경 관련 상태를 갖지 않으며, 이말은 곧 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;Unmodified 또는 Modified 상태인 파일은 곧 Tracked 상태&lt;/span&gt;인 것입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;Staged 상태&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Staged는 간단하게 Untracked 파일이나 Modified 상태인 파일을 Staging Area에 내역을 등록하지 않은 상태입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;백문이 불여일견 이라는 말도 있으니 다음의 git status 명령어 실습을 통해서 이해를 돕도록 하겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래에선 명령어 수준에서 상태에 대해 설명하겠습니다.&lt;/p&gt;
&lt;p&gt;아래에선 초기상태의 repo를 clone 받은 상황으로 first.txt 파일을 만들고 git status 명령어를 입력했을 때, 아직 git이 파일을 추적하지 않는 상황입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lifMx/btqZpfjWJlH/vBA5rGkyauxA9GEgeY96IK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lifMx/btqZpfjWJlH/vBA5rGkyauxA9GEgeY96IK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lifMx/btqZpfjWJlH/vBA5rGkyauxA9GEgeY96IK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlifMx%2FbtqZpfjWJlH%2FvBA5rGkyauxA9GEgeY96IK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;git add 명령어로 Git이 파일을 추적하도록 만들고 상태를 보겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EyAZE/btqZoyxhXHZ/aOkV7PmJBpYAuia50yExWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EyAZE/btqZoyxhXHZ/aOkV7PmJBpYAuia50yExWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EyAZE/btqZoyxhXHZ/aOkV7PmJBpYAuia50yExWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEyAZE%2FbtqZoyxhXHZ%2FaOkV7PmJBpYAuia50yExWK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;위 화면에서 git status 명령어 결과로&amp;nbsp;&lt;b&gt;Changes to be commited:&lt;/b&gt;&amp;nbsp;를 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;이 말은 파일이 staging area 영역에 있는 staged 상태입니다.&lt;/p&gt;
&lt;p&gt;파일을 맨 처음 git에 add 할 때는 staged 상태가 됩니다.&lt;/p&gt;
&lt;p&gt;commit 명령어를 통해 commited 상태로 repo에 저장했습니다.&lt;/p&gt;
&lt;p&gt;이상태에서 파일의 내용을 변경하겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DnST9/btqZkrr8raE/lKV5z6eLDhglB3zN7Wb0Hk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DnST9/btqZkrr8raE/lKV5z6eLDhglB3zN7Wb0Hk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DnST9/btqZkrr8raE/lKV5z6eLDhglB3zN7Wb0Hk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDnST9%2FbtqZkrr8raE%2FlKV5z6eLDhglB3zN7Wb0Hk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;위에서 단순히 파일을 변경할 경우, working directory 영역에서 modified 상태가 되며 commit을 할 수 없습니다. git add 명령어로 staged 상태로 변경해야만 commit이 가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;git status -s 를 통해 상태를 간략하게 확인하기&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;git status는 &lt;span style=&quot;letter-spacing: 0px; background-color: #9feec3;&quot;&gt;로컬저장소의 현재 상태를 보여줍니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;git status 명령어의 --short 옵션 또는 -s 옵션을 통해 상태를 간략히 볼 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1615001706677&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git status -s

??&amp;nbsp;firstFile.txt
A&amp;nbsp;&amp;nbsp;secondFile.txt
M&amp;nbsp;thirdFile.txt
M&amp;nbsp;&amp;nbsp;fourthFile.txt
MM&amp;nbsp;fifthFile.txt&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;-s, --short 옵션을 주게 되면 git status 명령만을 사용했을 때 나왔던 상태 메시지들을 두 자리의 문자로 표현합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;앞자리는 Staging Area 기준, 뒷 자리 문자는 워킹 디렉터리 기준입니다. 다음의 설명을 참고합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Tc63p/btqZlPNgQyr/GKjVj6AYnpvyGBpHQN0KsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Tc63p/btqZlPNgQyr/GKjVj6AYnpvyGBpHQN0KsK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Tc63p/btqZlPNgQyr/GKjVj6AYnpvyGBpHQN0KsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTc63p%2FbtqZlPNgQyr%2FGKjVj6AYnpvyGBpHQN0KsK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;참조:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://dololak.tistory.com/304&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;dololak.tistory.com/304&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://medium.com/sjk5766/git-3%EA%B0%80%EC%A7%80-%EC%83%81%ED%83%9C%EC%99%80-%EA%B0%84%EB%8B%A8-%EB%AA%85%EB%A0%B9%EC%96%B4-%EC%A0%95%EB%A6%AC-a80161aacec1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;medium.com/sjk5766/git-3%EA%B0%80%EC%A7%80-%EC%83%81%ED%83%9C%EC%99%80-%EA%B0%84%EB%8B%A8-%EB%AA%85%EB%A0%B9%EC%96%B4-%EC%A0%95%EB%A6%AC-a80161aacec1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://git-scm.com/book/ko/v2/Git%EC%9D%98-%EA%B8%B0%EC%B4%88-%EC%88%98%EC%A0%95%ED%95%98%EA%B3%A0-%EC%A0%80%EC%9E%A5%EC%86%8C%EC%97%90-%EC%A0%80%EC%9E%A5%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;git-scm.com/book/ko/v2/Git%EC%9D%98-%EA%B8%B0%EC%B4%88-%EC%88%98%EC%A0%95%ED%95%98%EA%B3%A0-%EC%A0%80%EC%9E%A5%EC%86%8C%EC%97%90-%EC%A0%80%EC%9E%A5%ED%95%98%EA%B8%B0&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://dimdim.tistory.com/entry/GIT%EC%97%90-%EB%8C%80%ED%95%9C-%EB%82%B4%EC%9A%A9%EC%A0%95%EB%A6%AC-%EC%A0%95%EB%A6%AC%EC%A4%91&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;dimdim.tistory.com/entry/GIT%EC%97%90-%EB%8C%80%ED%95%9C-%EB%82%B4%EC%9A%A9%EC%A0%95%EB%A6%AC-%EC%A0%95%EB%A6%AC%EC%A4%91&lt;/a&gt;&lt;/p&gt;</description>
      <category>git(분산 버전 관리 시스템)</category>
      <category>repository</category>
      <category>staging area</category>
      <category>working directory</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/154</guid>
      <comments>https://coding-lks.tistory.com/154#entry154comment</comments>
      <pubDate>Fri, 5 Mar 2021 22:10:49 +0900</pubDate>
    </item>
    <item>
      <title>django 개인 프로젝트[TOY42] 1 - 사용된 언어, 프레임워크와 사용한 이유</title>
      <link>https://coding-lks.tistory.com/153</link>
      <description>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;TOY42에서 사용된 프로그래밍 언어, DB, 프레임워크들을 왜 사용했는지 회고?&lt;/p&gt;
&lt;p&gt;무슨 이유로 사용하게 됬는지 더 좋은 대안은 없었는지 정리해보겠습니다!&amp;nbsp;&lt;/p&gt;
&lt;p&gt;언어는 python&lt;/p&gt;
&lt;p&gt;프레임워크는 django, bootstrap,&lt;/p&gt;
&lt;p&gt;DB는 mysql&lt;/p&gt;
&lt;p&gt;사용했으며 각자의 장단점을 알아보고 결정적으로 TOY42에 적용하게된 이유를 정리해봤습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;python&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8m1U6/btqZn1TRC4s/hkmsKQdZi6UmStHz0ET4EK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8m1U6/btqZn1TRC4s/hkmsKQdZi6UmStHz0ET4EK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8m1U6/btqZn1TRC4s/hkmsKQdZi6UmStHz0ET4EK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8m1U6%2FbtqZn1TRC4s%2FhkmsKQdZi6UmStHz0ET4EK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;b&gt;파이썬의 장점&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;1. 광범위한 라이브러리&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;파이썬은 광범위하게 라이브러리를 다운 받을수 있습니다. 라이브러리는 여러가지 목적에 맞게 쓰여진 코드들을 가지고 있으며, 이 코드들을 다운 받아서 사용할수 있습니다.&lt;/p&gt;
&lt;p&gt;예를들면, 정규표현식, 문서생성, 유닛테스팅, 웹브라우저, 쓰레딩, 데이터베이시, 이메일, 이미지 수정, 데이터분석 등등 목적에 맞게 파이썬 라이브러리를 다운받아서 사용할수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;2. 확장성&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;파이썬은 다른 언어로도 확장이 가능합니다.&lt;/p&gt;
&lt;p&gt;어느정도의 코드는 C++ 이나 C 로도 작성이 가능합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;4. 향상된 생산성&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;파이썬의 심플함과 광범위한 라이브러리의 사용으로. 파이썬은 Java 나 C++ 같은 언어보다 더 생산성이 높습니다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;코드는 조금쓰고 얻는건 더 많습니다&lt;/b&gt;&lt;/p&gt;
&lt;hr style=&quot;margin: 2em 5em; border-width: 0px 0px 1px; border-image: initial; color: #333333; font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; border-color: initial initial #cccccc initial; border-style: initial initial solid initial;&quot; /&gt;
&lt;p&gt;&lt;b&gt;6. 쉽고 간단함&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;자바같은 언어에서는, 'hello world'를 출력하기 위해서도 클래스를 사용해야 합니다. 이런 간단한 출력문에도 복잡한 클래스를 사용해야 한다는 뜻이죠. 하지만 파이썬에서는, print 문 하나면 해결이 됩니다.&lt;/p&gt;
&lt;p&gt;파이썬은 꽤나 쉽게 배울수 있고 이해할수 있고 코딩까지 할수 있습니다.&lt;/p&gt;
&lt;p&gt;배우기 쉽고 간단하기 때문에, 상대적으로 어려운 언어인 Java같은 언어 대신 파이썬을 선택합니다.&lt;/p&gt;
&lt;p&gt;비전공자한텐,, 그렇게 쉽지도 간단하지도 않습니다. 깊게 들어가면 어렵습니다.. 한없이... 많이.. 매우.. 장고쟁이&lt;/p&gt;
&lt;p&gt;타 언어에 비해서 코딩이 매우 간결합니다. 초심자에게 대체로 많이 권장되는 언어 입니다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;7. 가독성&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;파이썬을 읽는것은 그저 영문을 읽어내리는것과 비슷합니다.&lt;/p&gt;
&lt;p&gt;이 점이 바로 어째서 파이썬이 배우기 쉽고, 이해하기 쉽고, 코딩하기 쉬운 점입니다.&lt;/p&gt;
&lt;p&gt;또한, 파이썬은 불필요한 중괄호를 사용하지 않고 들여쓰기로 코드를 구분합니다&lt;/p&gt;
&lt;p&gt;복잡한 중괄호를 사용하는 대신에, 들여쓰기로 공간을 구분함으로써, 타 언어보다 가독성이 높은 편입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;8. 객체 지향 프로그래밍&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;파이썬은 객체 지향 프로그래밍 언어 입니다.&lt;/p&gt;
&lt;p&gt;함수들은 코드의 재사용성을 높이고. 클래스와 객체들은 현실세계에 있는것들을 추상화하고 모델링 하는데 도움이 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;하나의 클래스는 데이터와 함수를 하나로 캡슐화를 해줍니다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;객체 지향 프로그래밍의 개념과 클래스의 개념을 잘 알아야 합니다. 이 부분에 대해서 공부 열심히 해야합니다.&lt;br /&gt;장고쟁이&lt;/p&gt;
&lt;p&gt;&lt;b&gt;9. 무료 그리고 오픈소스&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;파이썬은 무료입니다! 공짜. 그리고, 파이썬은 오픈소스이기 때문에.&lt;/p&gt;
&lt;p&gt;다운 받아서 사용도 가능하지만, 다운받아서 소스코드를 수정한뒤 다시 배포할수 있습니다.&lt;/p&gt;
&lt;p&gt;추가로, 파이썬은 인터프리티드 언어 입니다. 파이썬에서 구문은 하나 하나씩 실행이 됩니다. 따라서, 디버깅이 다른 컴파일 언어보다 쉽습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;파이썬의 단점&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;1. 속도의 한계&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;파이썬은 코드가 한줄씩 실행 됩니다. 그리고, 파이썬은 인터프리티드 언어이기 때문에 실행이 느립니다.&lt;/p&gt;
&lt;p&gt;한줄 한줄 읽어가면서, 파이썬 코드를 컴퓨터가 이해할수 있는 언어로 해석하여 프로그램이 실행이 되기 때문에 실행이 컴파일 언어에 비해서 많이 느립니다.&lt;/p&gt;
&lt;p&gt;빠른 속도를 요하는 프로젝트에는 적합하지 않은 언어일수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;3. 디자인 제약&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;파이썬은 동적 타이핑 언어입니다. 이 말은, 파이썬에서는 변수를 선언할때 변수의 타입을 선언하지 않아도 된단 이야기 입니다. 파이썬은 duck-typing 을 사용합니다. 이것은 프로그래머들이 코딩할땐 편하지만,&lt;/p&gt;
&lt;p&gt;런타임 에러를 발생 시킵니다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.naver.com/PostView.nhn?blogId=headfat1218&amp;amp;logNo=221872912607&quot;&gt;https://blog.naver.com/PostView.nhn?blogId=headfat1218&amp;amp;logNo=221872912607&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;사용하게 된 이유&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;1. 다룰줄 아는 언어 중 웹 개발에 적합하며 웹프레임워크가 존재하는점&lt;/p&gt;
&lt;p&gt;2. 다룰줄 아는 언어 중 이해도가 가장 높은점&lt;/p&gt;
&lt;p&gt;3. 가독성! 파이썬을 좋아하는 이유 중 하나입니다!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Django&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QA0wu/btqZn0gmyDj/KUdjkvVK7QweRkCseThjLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QA0wu/btqZn0gmyDj/KUdjkvVK7QweRkCseThjLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QA0wu/btqZn0gmyDj/KUdjkvVK7QweRkCseThjLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQA0wu%2FbtqZn0gmyDj%2FKUdjkvVK7QweRkCseThjLK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1. 진입장벽이 낮은 프로그래밍 언어인 Python을 기반으로 한다.&lt;/p&gt;
&lt;p&gt;2. 인증, 관리와 같이 거의 대부분의 사이트에서 사용하는 기능들이 기본 모듈로 제공된다.&lt;/p&gt;
&lt;p&gt;3. 성공적인 도입 사례가 있습니다.(Instagram 등)&lt;/p&gt;
&lt;p&gt;4. 높은 코드 완성도를 유지할 수 있다.&lt;/p&gt;
&lt;p&gt;5. python의 강제된 indent가 코드 완성도에는 일조함.&lt;/p&gt;
&lt;p&gt;6. IDE 지원이 훌륭한 편이다.(Pycharm, Visual Studio 등)&lt;/p&gt;
&lt;p&gt;&lt;b&gt;7. AWS, Google Cloud, Azure등에서 전폭적으로 초기 단계부터 지원한 프레임워크다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp;-&amp;gt; deploy 및 운용에 대한 개발 비용을 크게 아낄 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;한글 문서가 아주 풍부한 편은 아니다.&lt;/p&gt;
&lt;p&gt;typeless 언어의 약점은 그대로 보유하고 있다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;python에 대한 높은 이해도가 필요하다 &amp;rarr; 실제로 데코레이터 개념에 대해서는 다시 한번 공부를 했다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;**.**Django는 문제를 python으로 해결하는 편이다. (프레임워크 특화 기능보다)&lt;/p&gt;
&lt;p&gt;&lt;b&gt;성능 문제에서 자유롭지 못하다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;python은 ruby보다 빠를 뿐이다.Django도 rails보다 빠를 뿐이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;출처:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://elky.tistory.com/652&quot;&gt;https://elky.tistory.com/652&lt;/a&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;사용하게된 이유&lt;/b&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;파이썬 기반 웹 프레임워크 이기 때문에&lt;/li&gt;
&lt;li&gt;빠르게 TOY42프로젝트를 구현하고 배포해보고 싶은 생각이었는데 실제로 django를 공부해보니 생산성이 좋다는 말을 체감할 수 있었다. &amp;rarr; flask와 django를 두고 고민하던 중 이 부분에 크게 매력을 느끼고 django로 결정했다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Pycharm을 사용하여 개발을 진행했는데 가상환경 구성, PRO버젼에서는 html문법 체크까지 해줘서 html,css등의 지식이 높은 수준이 아닌 나의 입장에서 오류를 해결하는데 시간을 많이 줄일 수 있었다.-&amp;gt; 6번 장점&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;성능 문제에서 자유롭지 못하다는 단점에서는 TOY42를 최종적으로는 42seoul API와 연동하여 구현할 계획이기 때문에 크게 장애가 되지 않을 것이라고 생각했다. -&amp;gt; 42seoul 인원은 많아진 지금도 1000명이 넘지 않기 때문에&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;/span&gt;
&lt;ol&gt;
&lt;li&gt;user 수 에서 자유로울 수 있다.&lt;/li&gt;
&lt;li&gt;게시판 형식으로 프로젝트 팀원을 구하는 TOY42에서 성능 문제를 저하시킬만큼 복잡한 로직을 쓰지 않을것 이다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;부트스트랩&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mxPAe/btqZqp7h3vQ/vCygzil3Q5NA6HKakID8Rk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mxPAe/btqZqp7h3vQ/vCygzil3Q5NA6HKakID8Rk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mxPAe/btqZqp7h3vQ/vCygzil3Q5NA6HKakID8Rk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmxPAe%2FbtqZqp7h3vQ%2FvCygzil3Q5NA6HKakID8Rk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Bootstrap 장단점&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;장점&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;부트스트랩의 가장 큰 장점은 그 내부의 클래스들만 알고 있다면 빠르고 쉽게 여러형태의 웹 페이지를 제작할 수 있다. 그리고 대부분은 각 해상도 대응에 대한 처리도 되어있기 때문에 반응형 처리도 어렵지 않다.&lt;/p&gt;
&lt;p&gt;퀼리티 또한 나쁘지 않기 때문에 많은 기업들이 사용하는 프레임워크이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;단점&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;디자인이 정형화되어 있어서 비슷한 디자인의 페이지가 양산될 수 있다. 사실 이 문제를 오픈 소스의 힘으로 극복할 수 있다. 부트스트랩은 오픈 소스이다 보니 사용자가 변경하는 것이 자유롭고, 변경한 것을 재배포하거나 상업적으로 판매하는 것이 가능하다. 변경한 테마를 내려받아 적용하는 것도 어렵지 않다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span&gt;이미지를 많이 쓰는 화려한 디자인을 부트스트랩으로 만들려면 소스를 처음부터 뜯어 고쳐 가면서 코딩을 해야 하다보니 복잡한 디자인에 사용하는 데에는 어려움이 있다. 원초부터 정형화된 디자인을 위해 있는 것이므로, 오히려 복잡한 사이트를 원한다면 그냥 안 쓰는 게 낫다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;사용하게 된 이유&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1. 위의 단점들보단 front end에 대한 지식수준이 낮은 나로써는 내부 클래스만 알면 빠르고 쉽게 웹페이지를 제작할 수 있다는 장점이 너무 크게 느껴졌다.&lt;/p&gt;
&lt;p&gt;정형화된 디자인의 페이지가 양상될 수 있다는 단점때문에 부트스트랩을 사용하는게 최선일까? 라는 생각을 가지고 있었지만&amp;nbsp; 실제로 TOY42프로젝트와 맞는 게시판 형식의 부트스트랩 테마를 발견하고부터는 이런 생각은 없어졌다.ㅋㅋ&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그리고 실제로 부트스트랩 테마를 구매해 42TOY프로젝트에 적용했고 django template 언어를 사용해 데이터를 주고 받는데 큰 어려움이 없었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;2. 부트스트랩은 &lt;span style=&quot;color: #333333;&quot;&gt;내부의 클래스들만 알고 있다면&lt;/span&gt; html과 css에 대한 고도의 숙련도를 요구하지 않는다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래는 42toy에 쓰인 부트스트랩 테마 링크이다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://w3layouts.com/template/animus-a-blogging-category-flat-bootstrap-responsive-web-template/&quot;&gt;https://w3layouts.com/template/animus-a-blogging-category-flat-bootstrap-responsive-web-template/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;참조 : &lt;a href=&quot;https://aimho.tistory.com/94&quot;&gt;https://aimho.tistory.com/94&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;MYSQL&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwbfDn/btqZpUmbigd/qrfnz6nXONsKgLEuzfFxH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwbfDn/btqZpUmbigd/qrfnz6nXONsKgLEuzfFxH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwbfDn/btqZpUmbigd/qrfnz6nXONsKgLEuzfFxH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwbfDn%2FbtqZpUmbigd%2Fqrfnz6nXONsKgLEuzfFxH0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;사실 처음 DB후보군에는 mysql과 postgresql두가지가 있었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;MySQL&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;오픈 소스로 무료로 사용 가능하다. O&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;top n개의 레코드를 가지고 오는 케이스에 특화되어 있다.O&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;update 성능이 postgre보다 우수하다.O&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Nested Loop Join만 지원한다.&lt;/p&gt;
&lt;p&gt;Nested Loop Join바깥 테이블의 처리 범위를 하나씩 접근하면서 추출된 값으로 안쪽 테이블을 조인하는 방식이다.중첩 루프문과 동일한 원리좁은 범위에 유리하다.순차적으로 처리한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;복잡한 알고리즘은 가급적 지원하지 않는다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;문자열 비교에서 대소 문자를 구분하지 않는다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;간단한 처리 속도를 향상 시키는 것을 추구함 O&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;간단한 데이터 트랜잭션을 위한 데이터베이스가 필요한 웹 기반 프로젝트에 널리 사용된다. 로드가 많거나 복잡한 쿼리는 성능이 저하된다.&lt;/p&gt;
&lt;p&gt;&amp;rarr; TOY42는 게시판형식의 간단한 로직들로 이루어질 거라 MYSQL이 적합하다고 생각했다.&lt;/p&gt;
&lt;p&gt;위 세가지 장점이 MYSQL을 선택하게된 강력한 이유들!(무료!)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;PostgreSQL&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;오픈 소스로 무료로 사용 가능하다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;다양한 join 방법을 제공한다.nested loop join, hash join, sort merge join결합 할 데이터가 많을 때는 hash, sort merge join데이터가 이미 정렬 되어 있는 경우에는 sort merge그렇지 않으면 hash join 추천&lt;/p&gt;
&lt;p&gt;sort merge join조인의 대상범위가 넓을 경우 발생하는 Random Access를 줄이기 위한 경우나 연결고리에 마땅한 인덱스가 존재하지 않을 경우 해결하기 위한 조인 방안양쪽 테이블의 처리범위를 각자 Access하여 정렬한 결과를 차례로 Scan하면서 연결고리의 조건으로 Merge하는 방식&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;hash join해시값을 이용하여 테이블을 조인하는 방식&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;update를 할 때, 과거 행을 삭제하고 변경된 데이터를 가진 새로운 행을 추가하는 형태라서 update가 느리다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;처리 속도를 빠르게 하기 위해 여러 CPU를 활용하여 쿼리를 실행한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;데이터베이스 클러스터 백업 기능을 제공한다.&lt;/p&gt;
&lt;p&gt;클러스터 : 디스크로부터 데이터를 읽어오는 시간을 줄이기 위해 자주 사용되는 테이블의 데이터를 디스크의 같은 위치에 저장시키는 방법&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;읽기, 쓰기 속도가 중요하고 데이터를 검증해야 하는 대규모 시스템에서 널리 사용된다.빈번한 update 성격일 경우 성능, 불안정 하지만 insert 위주 성격일 경우에는 적합하다.복잡한 쿼리를 실행해야하는 시스템에서 가장 잘 사용 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;출처 :&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@jisoo1170/Oracle-MySQL-PostgreSQL-차이점은&quot;&gt;https://velog.io/@jisoo1170/Oracle-MySQL-PostgreSQL-차이점은&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;사용하게 된 이유&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;대규모 시스템, 복잡한 쿼리에서 장점을 가지고 있는 postgresql OUT!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;사실 처음에는 postgresql을 사용했지만 pythonanywhere의 서비스를 이용해 배포를 했는데 postgresql사용시에는 유로이기 때문에 배포할 때 mysql로 바꾸게 되었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>프로젝트</category>
      <category>bootstrap</category>
      <category>Django</category>
      <category>mysql</category>
      <category>PYTHON</category>
      <category>TOY42</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/153</guid>
      <comments>https://coding-lks.tistory.com/153#entry153comment</comments>
      <pubDate>Fri, 5 Mar 2021 19:56:00 +0900</pubDate>
    </item>
    <item>
      <title>[git] git config</title>
      <link>https://coding-lks.tistory.com/152</link>
      <description>&lt;p&gt;git설치 후 초기설정시에 사용되는 명령어인 git config에 대해 알아 보겠습니다.&lt;/p&gt;
&lt;h3 id=&quot;_name&quot; data-ke-size=&quot;size23&quot;&gt;NAME&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;git-config - &lt;span style=&quot;color: #000000;&quot;&gt;리포지토리 또는 글로벌 옵션 가져오기 및 설정&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;_description&quot; data-ke-size=&quot;size23&quot;&gt;DESCRIPTION&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 명령을 사용하여 옵션을 쿼리/설정/변경/설정 해제할 수 있습니다. 이름은 실제로 섹션과 키로 점으로 구분되며 값은 이스케이프됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;git config&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Git의 사용 환경을 적절하게 설정해 주어야 한다. 한 번만 설정하면 된다. 설정한 내용은 Git을 업그레이드해도 유지된다.&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 때 git config를 사용해&lt;span style=&quot;color: #000000;&quot;&gt;&amp;nbsp;설정 내용을 확인하고 변경할 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1614929967898&quot; class=&quot;python&quot; style=&quot;margin: 20px auto 0px; display: block; overflow: auto; padding: 15px; color: #383a42; background: #f6f7f8; font-size: 14px; border-radius: 3px; font-family: Menlo, Consolas, Monaco, monospace; border: 1px solid #dddddd; cursor: default; z-index: 1;&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git config [옵션] 

옵션: 
      user.name &quot;유저 이름&quot; : 유저 이름 설정 
      user.email [메일주소] : 유저 메일주소 설정 
      --global : 전역 설정 
      -- list : 설정 목록 확인 &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용자 정보 설정&lt;/h3&gt;
&lt;p&gt;&lt;span&gt;Git을 설치하고 나서 가장 먼저 해야 하는 것은 사용자이름과 이메일 주소를 설정하는 것이다. Git은 커밋할 때마다 이 정보를 사용한다. 한 번 커밋한 후에는 정보를 변경할 수 없다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1614930146623&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git config --global user.name &quot;Lks&quot; 
$ git config --global user.email Lks@example.com&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span&gt;해당 시스템에서 해당 사용자가 사용할 때는 이 정보를 사용한다. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;만약 프로젝트마다 다른 이름과 이메일 주소를 사용하고 싶으면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;--global&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;옵션을 빼고 명령을 실행한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설정 확인&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1614930245141&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ git config --list 
user.name=Lks
user.email=Lks@gmail.com 
color.status=auto 
color.branch=auto 
color.interactive=auto 
color.diff=auto&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;참조:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&lt;a href=&quot;http://git-config.html&quot;&gt;git-config.html&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt;&lt;a href=&quot;https://git-scm.com/book/ko/v2/%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-Git-%EC%B5%9C%EC%B4%88-%EC%84%A4%EC%A0%95&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;git-scm.com/book/ko/v2/%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-Git-%EC%B5%9C%EC%B4%88-%EC%84%A4%EC%A0%95&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://goodtogreate.tistory.com/entry/Git-config-설정-계정설정&quot;&gt;https://goodtogreate.tistory.com/entry/Git-config-설정-계정설정&lt;/a&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>git(분산 버전 관리 시스템)</category>
      <category>config</category>
      <category>Git</category>
      <category>github</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/152</guid>
      <comments>https://coding-lks.tistory.com/152#entry152comment</comments>
      <pubDate>Fri, 5 Mar 2021 16:45:48 +0900</pubDate>
    </item>
    <item>
      <title>[git] git init</title>
      <link>https://coding-lks.tistory.com/151</link>
      <description>&lt;h3 id=&quot;_name&quot; data-ke-size=&quot;size23&quot;&gt;NAME&lt;/h3&gt;
&lt;pre id=&quot;code_1614928303676&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git init&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- 빈 Git 저장소를 생성하거나 기존 저장소를 다시 초기화합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;=&amp;gt; &lt;span&gt;현재 디렉토리를 새로운 Git 저장소로 설정&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;_synopsis&quot; data-ke-size=&quot;size23&quot;&gt;SYNOPSIS&lt;/h3&gt;
&lt;p&gt;git init [-q | --quiet] [--bare] [--template=&amp;lt;template_directory&amp;gt;] [--separate-git-dir &amp;lt;git dir&amp;gt;] [--object-format=&amp;lt;format&amp;gt;] [-b &amp;lt;branch-name&amp;gt; | --initial-branch=&amp;lt;branch-name&amp;gt;] [--shared[=&amp;lt;permissions&amp;gt;]] [directory]&lt;/p&gt;
&lt;pre id=&quot;code_1614929004585&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git init [-q | --quiet] [--bare] [--template=&amp;lt;template_directory&amp;gt;] 
	 [--separate-git-dir &amp;lt;git dir&amp;gt;] [--object-format=&amp;lt;format&amp;gt;] 
	 [-b &amp;lt;branch-name&amp;gt; | --initial-branch=&amp;lt;branch-name&amp;gt;] [--shared[=&amp;lt;permissions&amp;gt;]] 
         [directory]&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;_description&quot; data-ke-size=&quot;size23&quot;&gt;DESCRIPTION&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 명령은 빈 Git 저장소(기본적으로 개체, refs/heads, refs/tag 및 템플릿 파일의 하위 디렉토리가 있는 .git 디렉토리)를 생성합니다. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;커밋이 없는 초기 분기가 생성됩니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;기존 리포지토리에서 git init을 실행하는 것은 안전합니다. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;그것은 이미 있는 것들을 덮어쓰지 않을 것입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crCBst/btqZhfeeQQd/2nBQLPOdvo3CcxIr9yx4HK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crCBst/btqZhfeeQQd/2nBQLPOdvo3CcxIr9yx4HK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crCBst/btqZhfeeQQd/2nBQLPOdvo3CcxIr9yx4HK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcrCBst%2FbtqZhfeeQQd%2F2nBQLPOdvo3CcxIr9yx4HK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;git init를 다시해도 이미 지정된 저장소이기 때문에 저장소가 오염되지않는 것을 확인할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uaCDR/btqZfhQWvuf/CrpJbvzF5kViNKNUaQR5V0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uaCDR/btqZfhQWvuf/CrpJbvzF5kViNKNUaQR5V0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uaCDR/btqZfhQWvuf/CrpJbvzF5kViNKNUaQR5V0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuaCDR%2FbtqZfhQWvuf%2FCrpJbvzF5kViNKNUaQR5V0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;참조:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;a href=&quot;http://git-init.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;git-init.html&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>git(분산 버전 관리 시스템)</category>
      <category>Git</category>
      <category>git init</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/151</guid>
      <comments>https://coding-lks.tistory.com/151#entry151comment</comments>
      <pubDate>Fri, 5 Mar 2021 16:12:44 +0900</pubDate>
    </item>
    <item>
      <title>[git] Git? Github?</title>
      <link>https://coding-lks.tistory.com/150</link>
      <description>&lt;h2&gt;Git이란?&lt;/h2&gt;
&lt;p&gt;Git은 소프트웨어를 개발하는 기업의 핵심 자산인 소스코드를 효과적으로 관리할 수 있게 해주는&lt;b&gt;무료 공개소프트웨어&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;깃은 프로젝트의 어떤 부분도 겹쳐쓰지 않게 프로젝트의 변경을 관리하는 버전 분산 관리 시스템이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;Git을 사용하는 이유?&lt;/b&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;소스코드를 주고 받을 필요 없이, 같은 파일을&amp;nbsp;&lt;b&gt;여러 명이 동시에 작업하는 병렬 개발이 가능&lt;/b&gt;하다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;즉 브랜치를 통해 개발한 뒤, 본 프로그램에 합치는 방식(Merge)으로 개발을 진행할 수 있다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;한마디로 작업의 기록을 남겨서 수정 및 보완 등의 이력을 추적하기 위함이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;분산 버전관리이기 때문에 인터넷이 연결되지 않은 곳에서도 개발을 진행할 수 있으며, 중앙 저장소가 날라가버려도 다시 원상복구할 수 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;팀 프로젝트가 아닌, 개인 프로젝트일지라도 GIT을 통해 버전 관리를 하면 체계적인 개발이 가능해지고, 프로그램이나 패치를 배포하는 과정도 간단해집니다. (pull을 통한 업데이트, patch 파일 배포)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Git? Github?&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;간단히 Git은 로컬에서 버전 관리 시스템을 운영하는 방식이고 Github는 저장소를 깃허브에서 제공해주는 클라우드 서버를 이용한다는 것의 차이입니다.&lt;/p&gt;
&lt;p&gt;따라서 다른 사람들과 협업할 경우, 오픈소스를 공유하고 다른 사람들의 의견을 듣고 싶은 경우 등은 Github를 써서 편리하게 기능을 사용할 수 있습니다. 만약 혼자 작업하거나 폐쇄적인 범위 내에서의 협업이라면 Git만 사용해도 무방합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/X0qE1/btqZhgRMxOf/JztzjAnYncBUun0lPFBMxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/X0qE1/btqZhgRMxOf/JztzjAnYncBUun0lPFBMxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/X0qE1/btqZhgRMxOf/JztzjAnYncBUun0lPFBMxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FX0qE1%2FbtqZhgRMxOf%2FJztzjAnYncBUun0lPFBMxK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;기본 용어&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;커맨드 라인(Command Line)&lt;/b&gt;&amp;nbsp;깃 명령어를 입력할 때 사용하는 컴퓨터 프로그램. 맥에선 터미널이라고 한다. PC에선 기본적인 프로그램이 아니어서 처음엔 깃을 다운로드해야 한다(다음 섹션에서 다룰 것이다). 두 경우 모두 마우스를 사용하는 것이 아닌 프롬프트로 알려진 텍스트 기반 명령어를 입력한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;저장소(Repository)&lt;/b&gt;&amp;nbsp;프로젝트가 거주(live)할 수 있는 디렉토리나 저장 공간. 내 컴퓨터 안의 로컬 폴더가 될 수도 있고, 깃허브나 다른 온라인 호스트의 저장 공간이 될 수도 있다. 저장소 안에 코드, 텍스트, 이미지 파일 등을 저장하고 이름 붙일 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;버전관리(Version Control)&lt;/b&gt;&amp;nbsp;기본적으로 깃이 서비스 되도록 고안된 목적. 워드 작업할 때, 저장하면 이전 파일 위에 겹쳐쓰거나 여러 버전으로 나누어 저장하는데 깃은 그럴 필요가 없다. 프로젝트 히스토리의 모든 시점의 &amp;ldquo;스냅샷&amp;rdquo;을 유지하므로 결코 잃어버리거나 겹쳐쓰지 않을 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;커밋(Commit)&lt;/b&gt;&amp;nbsp;깃에게 파워를 주는 명령이다. 커밋하면, 그 시점의 나의 저장소의 &amp;ldquo;스냅샷&amp;rdquo;을 찍어, 프로젝트를 이전의 어떠한 상태로든 재평가하거나 복원할 수 있는 체크포인트를 가질 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;브랜치(Branch)&lt;/b&gt;&amp;nbsp;여러 명이 하나의 프로젝트에서 깃 없이 작업하는 것이 얼마나 혼란스러울 것인가? 일반적으로, 작업자들은 메인 프로젝트의 브랜치를 따와서(branch off), 자신이 변경하고 싶은 자신만의 버전을 만든다. 작업을 끝낸 후, 프로젝트의 메인 디렉토리인&amp;nbsp;&lt;b&gt;master&lt;/b&gt;에 브랜치를 다시&amp;nbsp;&lt;b&gt;Merge&lt;/b&gt;한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;참조:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://nolboo.kim/blog/2013/10/06/github-for-beginner/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;nolboo.kim/blog/2013/10/06/github-for-beginner/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@gpwls320/Git-%EC%9A%A9%EC%96%B4-%EB%AA%85%EB%A0%B9%EC%96%B4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;velog.io/@gpwls320/Git-%EC%9A%A9%EC%96%B4-%EB%AA%85%EB%A0%B9%EC%96%B4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://codevang.tistory.com/217&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;codevang.tistory.com/217&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>git(분산 버전 관리 시스템)</category>
      <category>Git</category>
      <category>github</category>
      <category>분산 버전 관리</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/150</guid>
      <comments>https://coding-lks.tistory.com/150#entry150comment</comments>
      <pubDate>Fri, 5 Mar 2021 15:38:25 +0900</pubDate>
    </item>
    <item>
      <title>도커(Docker)란 무었인가</title>
      <link>https://coding-lks.tistory.com/148</link>
      <description>&lt;p&gt;최근 클라우드 컴퓨팅이 인기를 끌면서 도커(Docker)라고하는 것이 인기를 끌고 있다. 도커에 대해서는 이야기만 듣고 있다가 최근 업무에서 실제 도커를 이용한 작업을 하게되면서 본격적으로 다루게 되었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://it.plusblog.co.kr/220979998005&quot;&gt;도커(Docker)에 대한 글 더 보기&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;도커(Docker)는 소프트웨어 컨테이너(Container) 플랫폼의 선두주자이다. 도커는 컨테이너 서비스를 지원하기 위한 플랫폼으로 도커를 이해하기 위해서는 컨테이너에 대해서 필수적으로 알아야 한다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://mblogthumb-phinf.pstatic.net/MjAxNzA0MTFfMTY1/MDAxNDkxOTIxMTU3MDk5.1qo8lNoq_jSRGxA5LV0ulyVXuqjYo2XocTk4oh1--GUg.HlDlF49ky62fsjy5_0Qpfwirlp0LOYJmxmjVcV7Xs74g.JPEG.complusblog/image_3772924781491921111535.jpg?type=w800&quot; alt=&quot;https://mblogthumb-phinf.pstatic.net/MjAxNzA0MTFfMTY1/MDAxNDkxOTIxMTU3MDk5.1qo8lNoq_jSRGxA5LV0ulyVXuqjYo2XocTk4oh1--GUg.HlDlF49ky62fsjy5_0Qpfwirlp0LOYJmxmjVcV7Xs74g.JPEG.complusblog/image_3772924781491921111535.jpg?type=w800&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;가상화와 가상머신(Virtual Machine)이란 무엇인가?&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;가상화란 애플리케이션의 서비스를 저렴한 비용으로 안정적이고 빠르게 제공하기 위한 인프라스트럭쳐(Infrastructure)이다. 물리적인 컴퓨팅 자원 (CPU, Disk, Network 등)을 가상화하여 여러개의 실행환경(Tenant;테넌트)이 공유하여 자원을 효율적으로 사용할 수 있게 해주는 것이다. 물리적인 컴퓨팅 자원을 가상화한다는 것은 각 테넌트들이 서로의 존재를 모르도록 격리되어야 함을 의미한다.&lt;/p&gt;
&lt;p&gt;일반적이고 쉽게 이해할 수 있는 가상화 기술로는 가상머신(Virtual Machine)이 있다. 가상머신은 호스트 운영체제 위에 가상의 하드웨어를 서비스하는 Hypervisor 같은 가상머신을 관리하는 별도의 소프트웨어가 돌아가고, 해당 소프트웨어가 제공하는 가상의 하드웨어에 게스트 운영체제를 설치해서 사용하는 방식이다. 우리가 잘 알고 있는 VMWare, Virtual Box 같은 가상머신 제품이 이와 같은 범주에 해당한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;가상머신의 단점은 다음과 같다&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;1. 완전한 운영체제가 설치되기 때문에 대량의 메모리가 필요하다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;우분투 리눅스 서버를 올리려고 할 때, 메모리상에 로드되는 내용만해도 500MB 정도가 소모된다. 예를 들어, 가상머신 10개에 똑같이 우분투 리눅스 서버를 올리려고 할 때, 5GB 정도의 메모리가 사용될 수 있다. 물리적인 메모리의 양이 작은 경우 쓰레싱(Thrashing)을 초래해서 성능 저하가 발생할 수도 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;2. CPU 성능 확보에 대한 문제가 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;서로다른 애플리케이션이 CPU 자원에 대한 요청을 할 때 CPU 자원에 대한 경쟁이 발생하게 된다. 이 때, 우선순위 등을 주어서 운영체제 내에서는 효율적으로 관리를 할 수 있지만 가상화된 CPU가 실제 물리 CPU를 어떻게 얻을지는 정해지지 않았다. 예를 들어 1번 게스트 OS에 서 우선순위가 높다고 해도, 게스트 운영체제 자체가 우선순위가 낮다면 물리적인 CPU 자원을 확보하는게 쉽지 않을 수 있다.이와 같은 원인으로 CPU 자원 경쟁에 따른 성능저하가 있을 수 있다. 가상화 서비스에서는 이런 문제를 해결하기위해 Dedicate 서비스를 제공한다. 즉, CPU 같은 물리적인 자원을 특정 게스트 운영체제에게 전담배치시키는 서비스이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;3. 시스템 운영에 대한 통합적인 지식이 필요하다&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;일반적인 시스템에서는 소프트웨어 개발과 시스템 운영이 분리가 되어 있었다. 하지만 가상머신체제에서는 이미 만들어진 시스템 위에 새로운 시스템을 구축하는 개념이기 때문에 통합적인 시스템 운영 지식과 소프트웨어 개발 지식이 통합 관리되어야하는 부담이 있다.&lt;/p&gt;
&lt;p&gt;가상머신은 이러한 태생적인 문제를 가지고 있다. 물론 호스트 운영체제와 게스트 운영체제가 달라도 된다는 장점 등이 있을 수 있다. 예를 들어 윈도우즈에 VM Ware를 설치하고, 리눅스를 설치할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://mblogthumb-phinf.pstatic.net/MjAxNzA0MTFfMjc2/MDAxNDkxOTIxMDExMzUz.vTc_6EhaS43yFyUY4dLDQjjfobbmUcNLQbpd6PgLBw8g.3G_A-911Wl1Ov2hJLAOSa5FS5j9Zzob-oy1QMfUj41Qg.JPEG.complusblog/%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88.jpg?type=w800&quot; alt=&quot;https://mblogthumb-phinf.pstatic.net/MjAxNzA0MTFfMjc2/MDAxNDkxOTIxMDExMzUz.vTc_6EhaS43yFyUY4dLDQjjfobbmUcNLQbpd6PgLBw8g.3G_A-911Wl1Ov2hJLAOSa5FS5j9Zzob-oy1QMfUj41Qg.JPEG.complusblog/%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88.jpg?type=w800&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;컨테이너(Container)란 무엇인가&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;가상머신의 생성 없이 호스트 운영체제에 애플리케이션을 같이 수행하면 어떨까? 애플리케이션을 하나의 운영체제에 같이 수행을 하는 경우엔 애플리케이션들이 디스크, 네트워크, CPU, 메모리 등을 공유하게되어 자원 활용과 보안 문제를 야기한다. 애플리케이션의 실행환경을 가상화하기 위해서는 애플리케이션이 사용하는 CPU, 메모리 등의 자원에 대한 쿼터(Quota)를 설정하고 관리할 수 있어야하며, 서로의 실행환경이 격리되어 있어야 한다.&lt;/p&gt;
&lt;p&gt;가상머신을 사용하는 경우엔 하드웨어들이 가상화되기 때문에 자연스럽게 실행환경이 격리되고, 각 가상 하드웨어를 제공할 때 쿼터를 제공할 수 있어 간단하다는 장점이 있다. 하지만 위에서 봤던 단점들이 있다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://mblogthumb-phinf.pstatic.net/MjAxNzA0MTFfMjUz/MDAxNDkxOTE5MDQ4Njcx.fgTE6DohRM5b8pcKyRr-U2NgH84yCa5aZX0qEpHGEQ8g.NmggoV8r88NvShwDkDtVwHQmNPnxRi0WFZYzMCBEzA4g.PNG.complusblog/Docker.png?type=w800&quot; alt=&quot;https://mblogthumb-phinf.pstatic.net/MjAxNzA0MTFfMjUz/MDAxNDkxOTE5MDQ4Njcx.fgTE6DohRM5b8pcKyRr-U2NgH84yCa5aZX0qEpHGEQ8g.NmggoV8r88NvShwDkDtVwHQmNPnxRi0WFZYzMCBEzA4g.PNG.complusblog/Docker.png?type=w800&quot; /&gt;&lt;/p&gt;
&lt;p&gt;)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://mblogthumb-phinf.pstatic.net/MjAxNzA0MTFfMjEz/MDAxNDkxOTE5MDQ4Njcx.DAA3zAXrZV5aSUIXNr37h6NM4wPnj7F-JOQ1W9hM1acg.-NgLf5ezgfp9zuxCTllIwXkZyQ2W3PeFpBHPpDMW_nog.PNG.complusblog/%EA%B0%80%EC%83%81%EB%A8%B8%EC%8B%A0.png?type=w800&quot; alt=&quot;https://mblogthumb-phinf.pstatic.net/MjAxNzA0MTFfMjEz/MDAxNDkxOTE5MDQ4Njcx.DAA3zAXrZV5aSUIXNr37h6NM4wPnj7F-JOQ1W9hM1acg.-NgLf5ezgfp9zuxCTllIwXkZyQ2W3PeFpBHPpDMW_nog.PNG.complusblog/%EA%B0%80%EC%83%81%EB%A8%B8%EC%8B%A0.png?type=w800&quot; /&gt;&lt;/p&gt;
&lt;p&gt;컨테이너 기반의 도커(상)와 가상머신(하)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;컨테이너는 호스트 운영체제의 커널을 공유하면서 격리된 컴퓨팅 자원을 제공하는 가상화 기술이다. Hypervisor 같은 소프트웨어로 하드웨어를 에뮬레이팅하고 그 위에 게스트 운영체제를 설치하는 가상머신과 달리 컨테이너는 호스트 운영체제만 필요로하고, 커널을 공유하여 동작한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;b&gt;&lt;i&gt;컨테이너 기술의 장점은 다음과 같다.&lt;/i&gt;&lt;/b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;1. 부팅이 빠르다.&lt;/b&gt;가상머신은 가상 하드웨어를 준비하고, 그 위에 게스트 운영체제를 부팅해야한다. 따라서 부팅 시간이 느리다.컨테이너는 게스트 운영체제라는 개념이 없고, 단지 컨테이너 플랫폼 위에 프로세스를 띄우는 정도만 수행하면 된다. 가상머신에 비해 컨테이너의 서비스 부팅 시간은 비교할 수 없을 정도로 차이가 난다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;2. 메모리 사용량이 적다&lt;/b&gt;가상머신은 게스트 운영체제가 별도로 실행되어야 한다. 이 운영체제를 위한 시스템 메모리만 수 백 MB를 소모하는데, 단순히 애플리케이션 수행을 위해 수 백 MB의 메모리를 소모해야하는 것은 심각한 오버헤드다.컨테이너는 게스트 운영체제를 띄우지 않기 때문에 딱 애플리케이션과 애플리케이션이 수행할 때 필요로 하는 라이브러리, 환경 구성 정도의 추가 메모리를 소모할 뿐이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;b&gt;&lt;i&gt;반면 컨테이너 기술은 다음과 같은 단점을 가지고 있다.&lt;/i&gt;&lt;/b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;1. 자원의 격리와 쿼터 제한이 어렵다&lt;/b&gt;가상머신은 가상 하드웨어를 직접 제어할 수 있기 때문에 높은 수준의 자원 격리와 쿼터 제한을 수행 할 수 있다.컨테이너의 경우 가상 하드웨어를 두고 있지 않기 때문에 격리 수준과 쿼터 제한이 가상 머신에 비해 떨어질 수 밖에 없다. 하지만 컨테이너 서비스 플랫폼들의 격리 기술과 쿼터 제한 기술이 발전하고 있기는 하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;2. 호스트 운영체제에 실행 환경이 묶인다.&lt;/b&gt;컨테이너는 호스트 운영체제의 커널을 공유하기 때문에 호스트 운영체제 환경을 그대로 가져갈 수 밖에 없다. 예를 들어, 리눅스에 컨테이너를 띄워서 윈도우즈 실행 환경을 만들 수 없다는 말이다.&lt;/p&gt;
&lt;p&gt;컨테이너 서비스에 이와 같은 장 단점이 존재한다. 하지만 클라우드 서비스 환경에서 컨테이너 기술을 사용하는 것이 가상머신을 사용하는 것에 비해 얻을 수 있는 장점이 압도적이기 때문에 많이 사용되고 있다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://mblogthumb-phinf.pstatic.net/MjAxNzA0MTFfMjIy/MDAxNDkxOTIwOTA0NDIz.OixMjEJC1MWVlM_jFn-ELElnHT8ueLeGYX3yxRz0fRYg.IzQlW_BpIgBUlXUlKcd1ZsZMC1LYcCc48X_Ia_aIdtog.PNG.complusblog/%EB%8F%84%EC%BB%A4.png?type=w800&quot; alt=&quot;https://mblogthumb-phinf.pstatic.net/MjAxNzA0MTFfMjIy/MDAxNDkxOTIwOTA0NDIz.OixMjEJC1MWVlM_jFn-ELElnHT8ueLeGYX3yxRz0fRYg.IzQlW_BpIgBUlXUlKcd1ZsZMC1LYcCc48X_Ia_aIdtog.PNG.complusblog/%EB%8F%84%EC%BB%A4.png?type=w800&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;도커(Docker)란?&lt;/h3&gt;
&lt;p&gt;도커는 2013년 3월 산타 클라라에서 열린 Pycon Conference에서 dotCloud의 창업자 Solomon Hykes가 발표한 The future of Linux Containers 라는 세션을 통해 세상에 처음 알려졌습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?time_continue=9&amp;amp;v=wW9CAH9nSLs&amp;amp;feature=emb_title&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/bRqCra/hyJq1uMsiO/KvGlab4qZdttH3YXblaukK/img.jpg?width=480&amp;amp;height=360&amp;amp;face=0_0_480_360&quot; data-video-width=&quot;480&quot; data-video-height=&quot;360&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/wW9CAH9nSLs&quot; width=&quot;480&quot; height=&quot;360&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;일반적으로 컨테이너는 배를 통해 물품들을 유통할 때 사용하는 커다란 수송용 박스를 의미한다. 각각의 컨테이너에는 옷, 술, 농산물 등이 들어 있을 수 있으며, 철저하게 규격화되어 있어 내부에 들어있는 물품에 상관없이 화물선이나 트레일러, 기차 등 다양한 운송수단에 쉽게 옮겨 실을 수 있다. (항만에 있는 크레인도 규격화 되어 있는 컨테이너를 쉽게 옮길 수 있다)&lt;/p&gt;
&lt;p&gt;컴퓨팅에서도 마찬가지로 생각할 수 있다. 애플리케이션을 실행, 관리할 수 있는 규격화된 컨테이너 안에는 실행할 수 있는 애플리케이션과 애플리케이션을 위한 라이브러리들이 들어있다. 이 컨테이너는 잘 규격화되어 있어 디스크에도 존재할 수 있고, 실행되면 메모리로 올라갈 수도 있다.&lt;/p&gt;
&lt;p&gt;도커(Docker)는 원래 항만에서 일하는 '부두 노동자'를 의미한다. 즉, 항만에서 잘 규격화되어 있는 컨테이너를 옮기고, 관리하는 직업을 도커(Docker)라고 하는 것이다.&lt;/p&gt;
&lt;p&gt;컴퓨팅에서의 도커의 역할도 이와 다르지 않다. 잘 만들어져있는 컨테이너 이미지를 메모리상에 띄우거나 컨테이너 내부에서 작업한 내용을 다시 이미지로 커밋하는 등, 가상화에서 사용하는 컨테이너를 다루는 일을 하는 소프트웨어가 도커(Docker)이다. (약간의 언어 유희라고 보면된다.)&lt;/p&gt;
&lt;p&gt;짧게 다시 말하면 도커는 컨테이너 기반의 오픈소스 가상화 플랫폼이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;출처 :&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://m.blog.naver.com/PostView.nhn?blogId=complusblog&amp;amp;logNo=220980996544&amp;amp;proxyReferer=https:%2F%2Fwww.google.com%2F&quot;&gt;m.blog.naver.com/PostView.nhn?blogId=complusblog&amp;amp;logNo=220980996544&amp;amp;proxyReferer=https:%2F%2Fwww.google.com%2F&lt;/a&gt;&lt;/p&gt;</description>
      <category>42seoul/ft_server</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/148</guid>
      <comments>https://coding-lks.tistory.com/148#entry148comment</comments>
      <pubDate>Wed, 3 Mar 2021 22:57:45 +0900</pubDate>
    </item>
    <item>
      <title>[네트워크] 연결 지향 VS 비연결 지향 프로토콜</title>
      <link>https://coding-lks.tistory.com/147</link>
      <description>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;비연결 프로토콜&lt;/b&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;연결을 유지하지 않는 프로토콜&lt;/li&gt;
&lt;li&gt;연결을 유지하는 비용이 들지 않아 더 싸다.&lt;/li&gt;
&lt;li&gt;매번 새롭게 연결이 성립되기 때문에 필요한 경우 매 연결 시 자신이 누구인지 알려줘야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;ex) HTTP 프로토콜을 이용한 웹 환경의 경우 쿠키나 세션을 통해 매번 자신을 식별할 수 있는 정보를 함께 전송한다.&lt;/p&gt;
&lt;p&gt;IP프로토콜은 비연결 프로토콜이지만 IP 프로토콜을 이용하는 TCP 프로토콜은 연결지향 프로토콜이다.&lt;/p&gt;
&lt;p&gt;또 TCP프로토콜을 이용하는 HTTP 프로토콜은 비연결 프로토콜이다.&lt;/p&gt;
&lt;p&gt;이렇듯 프로토콜을 어떻게 활용하느냐에 따라 연결 지향과 비연결 프로토콜로 바뀔 수 있다.&lt;/p&gt;
&lt;p&gt;비싸다고 무조건 나쁜 것이 아니므로 적절한 선택을 해야한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;비연결 서비스[connectionless service]&lt;/b&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;패킷은 연결 설정이나 연결 해체의 필요 없이 한쪽에서 다른 쪽으로 보내진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ex) 두 지역에 다리를 설치하지 않고 그냥 던지는 식으로 데이터를 전송&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;연결 지향 프로토콜&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;통신 연결이 유지되는 것을 지향하는 프로토콜&lt;/li&gt;
&lt;li&gt;연결을 계속 유지하기 위한 비용이 들기 때문에 더 비싸다.&lt;/li&gt;
&lt;li&gt;이미 연결이 되어있기 때문에 어떤 사람이 데이터를 보냈는지 연결을 이용하여 알 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ex)전화 통화는 이미 통화 연결이 성립될 때 서로 누군지 알기 때문에 연결이 유지되어 있기만 하면 시간이 지난 뒤 다시 말을 해도 누군지 알 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;연결 지향 서비스[connection-oriented service]&lt;/b&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;송신자와 수신자 사이에 먼저 연결이 설정이된 후 데이터가 전송된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ex) 두 지역에 다리를 설치해 놓고 데이터를 주고 받는 개념이다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;비연결 프로토콜은 데이터 그램 사이에 관계가 전혀 없는 독립된 데이터 그램이다.&lt;/li&gt;
&lt;li&gt;-&amp;gt;A[4]라는 데이터가 존재한다고 가정하자&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;A[1], A[2], A[3],A[4]와 같이 그냥 별개로 독립된 의미라는 것이다. 도착한 순서대로&lt;/p&gt;
&lt;p&gt;연결 지향 프로토콜은 데이터 그램 사이에 관계가 존재 하는 비독립적 데이터 그램&lt;/p&gt;
&lt;p&gt;이다.&lt;/p&gt;
&lt;p&gt;--&amp;gt; A[4]라는 데이터가 존재한다고 가정하자. 이 데이터들은 순서가 중요하다.&lt;/p&gt;
&lt;p&gt;그래서 A[1] A[2] A[3] A[4]와 같이 순서에 의미가 있는것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;출처 :&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://richong.tistory.com/78&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;richong.tistory.com/78&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://upcount.tistory.com/103&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;upcount.tistory.com/103&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>네트워크</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/147</guid>
      <comments>https://coding-lks.tistory.com/147#entry147comment</comments>
      <pubDate>Wed, 3 Mar 2021 22:37:31 +0900</pubDate>
    </item>
    <item>
      <title>[네트워크] 체크섬(Checksum)</title>
      <link>https://coding-lks.tistory.com/146</link>
      <description>&lt;p&gt;체크섬 - 신뢰성 없는 채널을 통한 통신이 이루어져 비트레어가 있는지 감지하는 방법&lt;/p&gt;
&lt;p&gt;&lt;b&gt;체크섬은&lt;/b&gt; 중복 검사의 한 형태이며&lt;/p&gt;
&lt;p&gt;송신된 자료의 무결성을 보호하는 단순한 방법이다.&lt;/p&gt;
&lt;p&gt;통신에서 CRC, 즉 순환 중복 검사를 체크섬이라고도 말하는데 엄밀히 따지면 체크섬은 나열된 데이터를 더하여 체크섬 숫자를 얻고, 정해진 비트 수의 모듈라로 정해진 비트 수로 재구성한다.&lt;/p&gt;
&lt;p&gt;체크섬은 기본적인 메시지 구성 요소를 추가해 결과값을 저장함으로 써 동작한다. 만약 체크섬이 맞아 떨어지지 않는다면 메시지가 손상되었다고 결론을 내릴 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;예시&lt;/h3&gt;
&lt;p&gt;4 바이트의 데이터가 있다. 0x25, 0x62, 0x3F, 0x52&lt;/p&gt;
&lt;p&gt;1단계 : 모든 바이트를 덧셈하면 0x118 이 된다.&lt;/p&gt;
&lt;p&gt;2단계 : 캐리 니블(1바이트의 절반으로 보통 4비트를 가리키는 컴퓨터 환경의 용어)을 버림으로써 0x18을 만든다.&lt;/p&gt;
&lt;p&gt;3단계 : 0x18의 2의 보수를 얻어 0xE8을 얻는데 이것이 바로 체크섬 바이트이다.&lt;/p&gt;
&lt;p&gt;체크섬 바이트를 테스트하려면 원래의 그룹의 바이트에 체크섬 바이트까지 더하면 0x200이 된다.&lt;/p&gt;
&lt;p&gt;다시 캐리 니블을 버림으로써 0x00이 된다. 0x00이라는 뜻은 오류가 없다는 뜻인데 오류가 있어도 우연히 될 수도 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;-출처 wikipedia&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;계산해보기&lt;/h3&gt;
&lt;p&gt;0x25, 0x62, 0x3F, 0x52를 10진수로 계산하자.&lt;/p&gt;
&lt;p&gt;0x25 = 37&lt;/p&gt;
&lt;p&gt;0x62 = 98&lt;/p&gt;
&lt;p&gt;0x3F = 63&lt;/p&gt;
&lt;p&gt;0x52 = 82&lt;/p&gt;
&lt;p&gt;모두 다 더하면 280이고 280을 다시 16진수로 변환시키면 0x118이다.&lt;/p&gt;
&lt;p&gt;여기서 캐리 니블을 버리도록 해보자. 0x118을 2진수로 변환시키면?&lt;/p&gt;
&lt;p&gt;계산하는 법은 그냥 1, 1, 8을 2진수로 변환시키면 된다. 만약 11A라면 A는 10으로 계산하는 식으로!&lt;/p&gt;
&lt;p&gt;어쨌든 118은 0001 0001 1000이 되게 된다.&lt;/p&gt;
&lt;p&gt;캐리니블, 즉 최상위 니블을 버리면 0001을 버리게 되니까 &lt;b&gt;0001 1000&lt;/b&gt;만 남게 된다.&lt;/p&gt;
&lt;p&gt;이제 2의 보수를 취하면 되는데&lt;br /&gt;2의 보수는 먼저 1의 보수로 바꾸고 거기에 +1을 하면 된다.&lt;/p&gt;
&lt;p&gt;해보자.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;0001 1000 -&amp;gt; 1110 0111 = 1의 보수&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;1110 0111 +1 1110 1000 = 2의 보수&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;1110 1000을 다시 16진법으로 표현하면 0xE8이 완성!&lt;br /&gt;이것을 바로 체크섬 바이트라고 한다는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;출처 :&lt;/span&gt;&lt;a href=&quot;https://hojak99.tistory.com/246&quot;&gt;hojak99.tistory.com/246&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이런식으로 한번 계산을 해보니 확실히 이해가 되었다.&lt;/p&gt;</description>
      <category>네트워크</category>
      <category>checksum</category>
      <category>네트워크</category>
      <category>중복검사</category>
      <category>체크섬</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/146</guid>
      <comments>https://coding-lks.tistory.com/146#entry146comment</comments>
      <pubDate>Wed, 3 Mar 2021 22:32:14 +0900</pubDate>
    </item>
    <item>
      <title>django 개인 프로젝트[TOY42] 1 - 개발과정 기록</title>
      <link>https://coding-lks.tistory.com/145</link>
      <description>&lt;p&gt;첫번째 django 개인 프로젝트를 하면서 느꼇던 점, 새롭게 배운 내용을 정리 해보았습니다.&lt;/p&gt;
&lt;p&gt;먼저 프로젝트 구현에 앞서서 기획을 했습니다.&lt;/p&gt;
&lt;p&gt;django를 배우고 부터 생각했던 토이프로젝트 모집 플랫폼! =&amp;gt; (토이프로젝트를 진행하기 위한 팀원을 모집하는 서비스)&lt;/p&gt;
&lt;p&gt;을 만들기로 결정하고&lt;/p&gt;
&lt;p&gt;어떤식으로 프로젝트를 구성해야 할지 고민중!&lt;/p&gt;
&lt;p&gt;다른 방법이 없을까 고민하던중 adobeXD라는 소프트웨어를 알게되었습니다.&lt;/p&gt;
&lt;p&gt;일단 어도비 XD는 사용자 체험 디자인 소프트웨어입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프로젝트 기획&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;노션을 이용하여 기획하기&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래 이미지는 실제로 프로젝트를 처음 기획할 때 작성했던 내용이다(허접...)&lt;/p&gt;
&lt;p&gt;처음 토이프로젝트 모집플랫폼을 만들어야지 django를 배우기 전부터이다.&lt;/p&gt;
&lt;p&gt;매번 개인프로젝트의 중요성을 들었기에!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wX2vb/btqYAsFbHqZ/llfQT07kHiaMxAEBdfODb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wX2vb/btqYAsFbHqZ/llfQT07kHiaMxAEBdfODb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wX2vb/btqYAsFbHqZ/llfQT07kHiaMxAEBdfODb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwX2vb%2FbtqYAsFbHqZ%2FllfQT07kHiaMxAEBdfODb0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;하지만 이런식으로 기획하고 구현을 진행하다보니 진행률이 너무 떨어졌다.&lt;/p&gt;
&lt;p&gt;예를 들어 진행하면서 위 이미지의 모집직군이라던지 현재 팀원 이런 데이터가 굳이 필요한지 이런 생각들이 들면서&lt;/p&gt;
&lt;p&gt;구현하고 수정하고 구현하고 수정하고 하며 1~2주일이 흘러 갔지만 만든건 accountapp(회원관련), articleapp(게시물관련)&lt;/p&gt;
&lt;p&gt;그러다가 adobeXD라는 소프트웨어를 알게 되었고 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;adobeXD를 이용하여 기획&lt;/span&gt;해봤다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AdobeXD를 활용하여 기획하기(강추)&lt;/h3&gt;
&lt;p&gt;ex) TOY42프로젝트를 adobe XD를 이용해 기획한 이미지&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/J233x/btqYJ9RSGCT/RxkNJfKGkEFZvg2ZFzry7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/J233x/btqYJ9RSGCT/RxkNJfKGkEFZvg2ZFzry7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/J233x/btqYJ9RSGCT/RxkNJfKGkEFZvg2ZFzry7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJ233x%2FbtqYJ9RSGCT%2FRxkNJfKGkEFZvg2ZFzry7K%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E90JX/btqYvd9RBqz/VHqipoV9qNA98DQumXmcTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E90JX/btqYvd9RBqz/VHqipoV9qNA98DQumXmcTk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E90JX/btqYvd9RBqz/VHqipoV9qNA98DQumXmcTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE90JX%2FbtqYvd9RBqz%2FVHqipoV9qNA98DQumXmcTk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;또한 위 이미지처럼 다중플로우 기능(&lt;span&gt;서로 다른 화면을 연결하기 위해 와이어를 드래그할 수 있는 플로우&lt;/span&gt;)을 사용하여&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;프로토타입을 만들어내어 생산성을 높여준다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1. 단순히 노션이나 메모장등을 이용해 기획하는 것보다 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;구체적으로 눈에 보이게 기획하는 것의 효과&lt;/span&gt;는 어마어마 했다.&lt;/p&gt;
&lt;p&gt;2. adobeXD를 활용하여 먼저 기획해 놓으니 django를 공부하며 배운 내용을 구체적으로 어떤 기능을 구현할 때 적용해야할지 더욱 구체적으로 생각하게 된다.&lt;/p&gt;
&lt;p&gt;3. 다중 플로우 기능을 이용해 프로토 타입을 만들 수 있는 기능을 사용해 어떤 기능을 구현해야할 지 더 구체적으로 알 수 있었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;ex) 다중 플로우 기능을 이용한 프로토 타입&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgyxeD/btqYDllkNGk/oZyYvFih1vn32v2JhkMNt0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgyxeD/btqYDllkNGk/oZyYvFih1vn32v2JhkMNt0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgyxeD/btqYDllkNGk/oZyYvFih1vn32v2JhkMNt0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgyxeD%2FbtqYDllkNGk%2FoZyYvFih1vn32v2JhkMNt0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;노션에서 작성했던 기획이 너무 부실하다보니 adobeXD를 이용해 기획할 때는 구체적이지 않은 부분들이 너무 많아서 간단한 프로젝트임에도 3일정도가 걸렸다...&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기획 결과&lt;/h3&gt;
&lt;p&gt;기획의 결과로&amp;nbsp;&lt;/p&gt;
&lt;p&gt;앱단위&lt;/p&gt;
&lt;p&gt;1. Accountapp - 회원관련기능&lt;/p&gt;
&lt;p&gt;2. Articleapp&amp;nbsp; &amp;nbsp;- 프로젝트 모집 관련 기능&lt;/p&gt;
&lt;p&gt;3. Applyapp&amp;nbsp; &amp;nbsp; - 프로젝트 지원 관련 기능&lt;/p&gt;
&lt;p&gt;4. Commentapp - 댓글 관련 기능&lt;/p&gt;
&lt;p&gt;이런 식으로 네개의 app을 가진 프로젝트를 구상하게 되었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;첫 번째 개인 프로젝트 이지만 adobeXD라는 소프트웨어를 이용하니 구현할 기능들을 더 구체화할 수 있었고 이로인해 구현 사이의 다음 기능을 어떻게 구현할지 고민하게 되는 시간이 줄어드는게 체감이 되었다!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;솔직히 이전에는 기획을 솔직히 중요하게 생각 안했고 굳이 필요하나??&lt;/p&gt;
&lt;p&gt;이런 생각을 가지고 있었는데 기획을 잘해야 생산성이 증가하겠다라는 생각이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;마지막으로&lt;/p&gt;
&lt;p&gt;TOY42의 메인 페이지 이미지입니다!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tO89W/btqYLyYbQAI/EKNzSM8h6MS2CuzUXurZkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tO89W/btqYLyYbQAI/EKNzSM8h6MS2CuzUXurZkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tO89W/btqYLyYbQAI/EKNzSM8h6MS2CuzUXurZkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtO89W%2FbtqYLyYbQAI%2FEKNzSM8h6MS2CuzUXurZkk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;다음 포스팅 에서는 개발환경과&amp;nbsp; Accountapp 구현 관련 포스팅을 하겠습니다!&lt;/span&gt;&lt;/p&gt;</description>
      <category>프로젝트</category>
      <category>adobeXD</category>
      <category>Django</category>
      <category>TOY42</category>
      <category>개인프로젝트</category>
      <category>토이프로젝트</category>
      <category>프로젝트기획</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/145</guid>
      <comments>https://coding-lks.tistory.com/145#entry145comment</comments>
      <pubDate>Fri, 26 Feb 2021 23:24:43 +0900</pubDate>
    </item>
    <item>
      <title>[django] static 파일 설정</title>
      <link>https://coding-lks.tistory.com/144</link>
      <description>&lt;h3&gt;&lt;b&gt;Static 파일이란?&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;static 파일이란 js, css, image, font 등과 같이 개발자가 사전에 미리 서버에 저장 해둔 파일들을 말합니다. 정적인 파일들이라고 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;static 사용을 위한 settings.py설정!&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;1. static_url 설정하기&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt; STATIC_URL = '/static/'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 구문을 추가해 설정할 수 있으며,&lt;/p&gt;
&lt;p&gt;이를 통해 각 static 파일에 대한 URL의 고정값을 설정할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;ex) /static/img/ 디렉토리안의 logo.jpg 파일을 사용하려한다면&lt;/p&gt;
&lt;pre class=&quot;vala&quot;&gt;&lt;code&gt;# example.html

{% static '/img/logo.jpg' %} &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이런식으로 static파일에 대해 참조할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;2. STATICFILES_DIRS&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;makefile&quot;&gt;&lt;code&gt;STATICFILES_DIRS = [
    BASE_DIR / &quot;static&quot;,
]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;프로젝트 전반적으로 사용되는 static 경로가 어딘지 설정합니다.&lt;/p&gt;
&lt;p&gt;프로젝트 디렉토리 바로 하위 단계에 만들어지며 위 코드에서는 &lt;span style=&quot;color: #333333;&quot;&gt;static&lt;/span&gt;라는 이름의 디렉토리가 생성됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;3. STATIC_ROOT&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;각 &lt;code&gt;static&lt;/code&gt; 파일들은 각자 다른 경로에 나눠져 있습니다. 왜냐하면 프로젝트 전반적으로 사용하는 파일들은 &lt;code&gt;STATICFILES_DIRS&lt;/code&gt; 에 담겨 있고, 각자의 app 안에는 app 에서 사용되는 파일들이 따로 모여있습니다.&lt;/p&gt;
&lt;p&gt;배포를 하기 위해서는 이들을 하나의 디렉토리에 모아야 하는데 아래 명령어로 한 번에 모을 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;python manage.py collectstatic&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;하지만 어디로 모을지는 따로 지정을 해줘야하고 그 경로가 바로 &lt;code&gt;STATIC_ROOT&lt;/code&gt; 입니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;static&lt;/code&gt; 폴더를 따로 만들지 않더라도 지정한 경로에 만들어주고 그 안에 복사하여 담아줍니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;만약 static폴더에 정적인 파일들을 사용하고 collectstatic을 사용했다면&lt;/p&gt;
&lt;p&gt;아래와 같은 프로젝트 구조가 될것이다!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;--------&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;--------&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;--------&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;--------&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;--------&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;--------&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;--------&lt;span style=&quot;color: #333333;&quot;&gt;--------&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;프로젝트 폴더/&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;config/&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;apps/&lt;/b&gt;
&lt;ul&gt;
&lt;li&gt;{app_name_1}/&lt;/li&gt;
&lt;li&gt;{app_name_2}/&lt;/li&gt;
&lt;li&gt;{app_name_3}/&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;templates/&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;static/&lt;/b&gt;
&lt;ul&gt;
&lt;li&gt;css/&lt;/li&gt;
&lt;li&gt;font/&lt;/li&gt;
&lt;li&gt;img/&lt;/li&gt;
&lt;li&gt;js/&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;staticfiles/&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;--------&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;--------&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;--------&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;--------&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;--------&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;--------&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;--------&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;--------&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;templates 에서 사용&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;django&quot;&gt;&lt;code&gt;{% load static %}

{% static 'STATIC_URL 이후의 경로' %}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이런식으로 스태틱 경로의 어떤 파일을 사용할 것인지 명시 해줘야한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;명시 한후 아래와 같이 {% static %} 혹은 경로를 직접입력하여 사용하면 된다!&lt;/p&gt;
&lt;pre class=&quot;django&quot;&gt;&lt;code&gt;&amp;lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;{%  static 'base.css' %}&quot; &amp;gt;

&amp;lt;img src=&quot;/static/images/logo.png&quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;</description>
      <category>django</category>
      <category>Django</category>
      <category>static</category>
      <category>정적 파일</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/144</guid>
      <comments>https://coding-lks.tistory.com/144#entry144comment</comments>
      <pubDate>Thu, 25 Feb 2021 19:31:16 +0900</pubDate>
    </item>
    <item>
      <title>[python] modul(모듈)</title>
      <link>https://coding-lks.tistory.com/143</link>
      <description>&lt;h3&gt;&lt;b&gt;1. 모듈&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;모듈(Module)은 파이썬 코드를 논리적으로 묶어서 관리하고 사용할 수 있도록 하는 것으로, 보통 하나의 파이썬 .py 파일이 하나의 모듈이 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;모듈 안에는 함수, 클래스, 혹은 변수들이 정의될 수 있으며, 실행 코드를 포함할 수도 있다.&lt;/p&gt;
&lt;p&gt;파이썬은 기본적으로 상당히 많은 표준 라이브러리 모듈들을 제공하고 있으며, 3rd Party에서도 많은 파이쎤 모듈들을 제공하고 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이러한 모듈들을 사용하기 위해서는 모듈을 import하여 사용하면 되는데, import 문은 다음과 같이 하나 혹은 복수의 모듈을 불러들일 수 있고 별칭을 붙일 수도 있다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;import 모듈1
import 모듈1, 모듈2, 모듈3 ...
import 모듈명 as 별칭
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;예를 들어, 아래 예제는 표준 라이브러리 중 수학과 관련된 함수들을 모아 놓은 &quot;math&quot; 모듈을 import 하여 그 모듈 안에 있는 factorial() 함수를 사용하는 예이다.&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;import math
n = math.factorial(5)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;하나의 모듈 안에는 여러 함수들이 존재할 수 있는데, 이 중 하나의 함수만을 불러 사용하기 위해서는 아래와 같이 &quot;from 모듈명 import 함수명&quot;이라는 표현을 사용할 수 있다. 이렇게 from...import... 방식으로 import 된 함수는 호출시 &quot;모듈명.함수명&quot;이 아니라 직접 &quot;함수명&quot;만을 사용한다.&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;# math모듈의 factorial 함수만 import
from math import factorial
n = factorial(5) / factorial(3)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;하나의 모듈 안에는 있는 여러 함수를 사용하기 위해 from... import (함수1, 함수2) 와 같이 import 뒤에 사용할 함수를 나열할 수도 있다.&lt;/p&gt;
&lt;p&gt;또한, 모든 함수를 불러 사용하기 위해서는 &quot;from 모듈명 import *&quot; 와 같이 별표(*)를 사용할 수 있다.&lt;/p&gt;
&lt;p&gt;이렇게 from...import... 방식으로 import 된 함수는 호출시 모듈명 없이 직접 함수명을 사용한다.&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;# 여러 함수를 import
from math import (factorial,acos)
n = factorial(3) + acos(1)

# 모든 함수를 import
from math import *
n = sqrt(5) + fabs(-12.5)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;함수의 이름이 길거나 어떤 필요에 의해 함수의 이름에 별칭(예시에서는 f)을 주고 싶은 경우가 있는데, 이 때는 아래와 같이 &quot;함수명 as 별칭&quot; 과 같은 표현을 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;# factorial() 함수를 f()로 사용 가능
from math import factorial as f
n = f(5) / f(3)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;2. 모듈의 위치&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;파이썬에서 모듈을 import 하면 그 모듈을 찾기 위해 다음과 같은 경로를 순서대로 검색한다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;현재 디렉토리&lt;/li&gt;
&lt;li&gt;현재 디렉토리에서 모듈이 발견되지 않으면 Python은 쉘 변수 PYTHONPATH의 각 디렉토리를 검색합니다.&lt;/li&gt;
&lt;li&gt;위의 방법이 실패하면 Python은 기본 경로를 확인합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;모듈 검색 경로는 시스템 모듈 sys에 sys.path 변수로 저장됩니다.&lt;/p&gt;
&lt;p&gt;sys.path 변수에 리스트 형태로 확인해보면 현재 디렉토리, PYTHONPATH 및 설치 종속 기본값이 들어 있습니다.&lt;/p&gt;
&lt;p&gt;또한 sys.path를 사용하기 위해서는 sys라는 시스템 모듈을 import 해야 하며, sys.path는 임의로 수정할 수도 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;예를 들어, 기존 sys.path에 새 경로를 추가(append)하면, 추가된 경로도 이후 모듈 검색 경로에 포함된다&lt;/p&gt;
&lt;p&gt;모듈을 import 하면 sys.path에 있는 경로 순서대로 모듈을 찾아 import하다가 만약 끝까지 찾지 못하면 에러가 발생된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;다음은 Windows, Linux 시스템에서 일반적인 PYTHONPATH입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Windows&lt;/p&gt;
&lt;pre class=&quot;crystal&quot;&gt;&lt;code&gt;set PYTHONPATH = c:\python20\lib;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Linux&lt;/p&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;set PYTHONPATH = /usr/local/lib/python
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래는 sys.path를 출력해 본 예인데, sys.path[0]의 값은 빈 문자열(empty string)로 이는 현재 디렉토리를 가리킨다.&lt;/p&gt;
&lt;p&gt;즉, 먼저 현재 디렉토리부터 찾는다는 뜻이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;마지막 라인은 sys.path.append()를 사용하여 새 경로를 추가하는 예를 든 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2021-02-23 오후 11.13.24.png&quot; data-origin-width=&quot;1138&quot; data-origin-height=&quot;234&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/th1J4/btqYpWSFxNc/03htqLXvDukpfInkOHx09K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/th1J4/btqYpWSFxNc/03htqLXvDukpfInkOHx09K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/th1J4/btqYpWSFxNc/03htqLXvDukpfInkOHx09K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fth1J4%2FbtqYpWSFxNc%2F03htqLXvDukpfInkOHx09K%2Fimg.png&quot; data-filename=&quot;스크린샷 2021-02-23 오후 11.13.24.png&quot; data-origin-width=&quot;1138&quot; data-origin-height=&quot;234&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;3. 모듈의 작성&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;프로그램을 모듈로 나누어 코딩하고 관리하는 것은 종종 많은 장점이 있다.&lt;/p&gt;
&lt;p&gt;사용자 함수 혹은 클래스를 묶어 모듈화하고, 이를 불러 사용하는 방법을 간략히 살펴보자.&lt;/p&gt;
&lt;p&gt;우선 아래 두 개의 함수(add와 substract)를 mylib.py 라는 모듈에 저장한다.&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;# mylib.py
def add(a, b):
        return a + b

def substract(a, b):
        return a - b&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;모듈 mylib.py가 있는 디렉토리에서 그 모듈을 import 한 후, mylib의 함수들을 사용한다.&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;# exec.py
from mylib import *
i = add(10,20)
i = substract(20,5)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;파이썬 모듈 .py 파일은 import 하여 사용할 수 있을 뿐만 아니라, 해당 모듈 파일 안에 있는 스크립트(작성된 .py 파일의 내용) 전체를 바로 실행할 수도 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;4. __ name__를 체크하는 이유&lt;/h3&gt;
&lt;p&gt;파이썬에서 하나의 모듈을 import 하여 사용할 때와 스크립트 전체를 실행할 때를 동시에 지원하기 위하여 흔히 관행적으로 모듈 안에서 &lt;b&gt;name&lt;/b&gt; 을 체크하곤 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;예를 들면 아래같은 코드를 종종 본적이 있을 거다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;if __name__ == '__main__':
   코드&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;도대체 이 코드는 왜 사용하는 것일까요?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 코드는 현재 스크립트 파일이 실행되는 상태를 파악하기 위해 사용합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;먼저 __&lt;b&gt;name__&lt;/b&gt;부터 알아보겠습니다.&lt;/p&gt;
&lt;p&gt;다음 내용을 프로젝트 폴더(C:\project) 안에 hello.py 파일로 저장하세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;hello.py&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;print('hello 모듈 시작')
print('hello.py __name__:', __name__)    # __name__ 변수 출력
print('hello 모듈 끝')&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그리고 다음 내용을 프로젝트 폴더(C:\project) 안에 main.py 파일로 저장한 뒤 실행해보세요.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;main.py&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;import hello    # hello 모듈을 가져옴

print('main.py __name__:', __name__)    # __name__ 변수 출력&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;실행 결과&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;markdown&quot;&gt;&lt;code&gt;hello 모듈 시작
hello.py __name__: hello
hello 모듈 끝
main.py __name__: __main__&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;실행을 해보면 hello.py 파일과 main.py 파일의 &lt;b&gt;name&lt;/b&gt; 변수 값이 출력됩니다.&lt;/p&gt;
&lt;p&gt;파이썬에서 import로 모듈을 가져오면 해당 스크립트 파일이 한 번 실행됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;따라서 hello 모듈을 가져오면 hello.py 안의 코드가 실행됩니다.&lt;/p&gt;
&lt;p&gt;따라서 hello.py의 &lt;b&gt;name&lt;/b&gt; 변수에는 'hello'가 들어가고, main.py의 &lt;b&gt;name&lt;/b&gt; 변수에는 '&lt;b&gt;main&lt;/b&gt;'이 들어갑니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;하지만 다음과 같이 &lt;b&gt;python&lt;/b&gt;으로 hello.py 파일을 실행해보면 결과가 조금 달라집니다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;C:\project&amp;gt;python hello.py
hello 모듈 시작
hello.py __name__: __main__
hello 모듈 끝&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;hello.py 파일의 &lt;b&gt;name&lt;/b&gt; 변수에는 'hello'가 아니라 '&lt;b&gt;main&lt;/b&gt;'이 들어갑니다.&lt;/p&gt;
&lt;p&gt;즉, 어떤 스크립트 파일이든 파이썬 인터프리터가 최초로 실행한 스크립트 파일의 &lt;b&gt;name&lt;/b&gt;에는 '&lt;b&gt;main&lt;/b&gt;'이 들어갑니다.&lt;/p&gt;
&lt;p&gt;이는 프로그램의 시작점(entry point)이라는 뜻입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;파이썬은 최초로 시작하는 스크립트 파일과 모듈의 차이가 없습니다.&lt;/p&gt;
&lt;p&gt;어떤 스크립트 파일이든 시작점도 될 수 있고, 모듈도 될 수 있습니다.&lt;/p&gt;
&lt;p&gt;그래서 &lt;b&gt;name&lt;/b&gt; 변수를 통해 현재 스크립트 파일이 시작점인지 모듈인지 판단합니다.&lt;/p&gt;
&lt;p&gt;if &lt;b&gt;name&lt;/b&gt; == '&lt;b&gt;main&lt;/b&gt;':처럼 &lt;b&gt;name&lt;/b&gt; 변수의 값이 '&lt;b&gt;main&lt;/b&gt;'인지 확인하는 코드는 현재 스크립트 파일이 프로그램의 시작점이 맞는지 판단하는 작업입니다.&lt;/p&gt;
&lt;p&gt;즉, &lt;span style=&quot;background-color: #9feec3;&quot;&gt;스크립트 파일이 메인 프로그램으로 사용될 때와 모듈로 사용될 때를 구분하기 위한 용도&lt;/span&gt;입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;참조:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://dojang.io/mod/page/view.php?id=2448&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;dojang.io/mod/page/view.php?id=2448&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://pythonstudy.xyz/python/article/17-%EB%AA%A8%EB%93%88-Module&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;pythonstudy.xyz/python/article/17-%EB%AA%A8%EB%93%88-Module&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>python</category>
      <category>module</category>
      <category>PYTHON</category>
      <category>__name__</category>
      <category>모듈</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/143</guid>
      <comments>https://coding-lks.tistory.com/143#entry143comment</comments>
      <pubDate>Tue, 23 Feb 2021 23:19:29 +0900</pubDate>
    </item>
    <item>
      <title>requirements.txt를 통한 의존성 관리</title>
      <link>https://coding-lks.tistory.com/142</link>
      <description>&lt;h3&gt;requirements.txt 란?&lt;/h3&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;# 예시
asgiref==3.3.1
beautifulsoup4==4.9.3
Django==3.1.5
django-bootstrap4==2.3.1
django-environ==0.4.5
Pillow==8.1.0
psycopg2==2.8.6
pytz==2020.5
soupsieve==2.1
sqlparse==0.4.1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;한 마디로 얘기하자면 &lt;b&gt;python 프로젝트의 의존성 정보가 담긴 문서&lt;/b&gt;이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;의존성 정보를 requirements.txt에 작성하는 이유는 협업이나 오픈 소스 등 다른 사람이(혹은 자신이 다른 환경에서) 해당 프로젝트를 실행할 일이 있을 때 편의를 위해서라고 생각한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;프로젝트를 실행하기 위해 프로젝트에서 사용한 패키지를 해당 환경에 모두 설치해야 하는데, 이러한 의존성 문제를 requirements.txt 파일을 이용하면 손쉽게 해결할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;의존성 정보는 위의 예시와 같이 사용된 패키지와 버전 정보로 나타내는데, 몇가지 형식이 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위와 같이 == 는 정확히 해당 버전을 설치하라는 얘기이고, == 대신 &amp;gt;, &amp;lt; 를 사용하면 해당 버전 이상이나 이전 버전을 설치하라는 얘기이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;또한 2.* 와 같이 버전 정보를 와일드카드를 사용해 작성할 수도 있다. (2.* = 2.대의 아무 버전이나 가능하다는 의미)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이름이 꼭 requirements.txt일 필요는 없지만, 관례상 사용하는 이름이다. 이름만 봐도 의미를 알 수 있게끔 관례를 따르자!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;작성 방법&lt;/h3&gt;
&lt;p&gt;작성 방법도 간단하다.&lt;/p&gt;
&lt;p&gt;먼저 현재 프로젝트의 의존성 정보를 알수 있는 명령어!&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;pip freeze&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;pip freeze는 현재 프로젝트에 설치된 패키지들의 버전 정보를 출력해주는 명령어이고,&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;명령어를 입력하면 아래예시와 같은 패키지들의 버전 정보를 출력해준다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;asgiref==3.3.1
beautifulsoup4==4.9.3
Django==3.1.5
django-bootstrap4==2.3.1
django-environ==0.4.5
Pillow==8.1.0
psycopg2==2.8.6
pytz==2020.5
soupsieve==2.1
sqlparse==0.4.1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이를 requirements.txt 파일에 저장하면 간편히 의존성 정보 파일을 만들 수 있다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;pip freeze &amp;gt; requirements.txt&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h3&gt;설치 방법&lt;/h3&gt;
&lt;p&gt;그리고 requirements.txt의 정보들을 하나씩 일일이 설치하지 않고&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;pip install -r requirements.txt&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;명령어를 사용하면 다른 환경에서도 현재 설치되있지 않은 패키지만 빠르게 설치할 수 있다!!!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;출처:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://enant.tistory.com/28&quot;&gt;https://enant.tistory.com/28&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://ssungkang.tistory.com/entry/Django-requirementstxt-%EB%A5%BC-%ED%86%B5%ED%95%9C-%ED%8C%A8%ED%82%A4%EC%A7%80-%EC%A2%85%EC%86%8D%EC%84%B1-%EA%B4%80%EB%A6%AC?category=320582&quot;&gt;https://ssungkang.tistory.com/entry/Django-requirementstxt-를-통한-패키지-종속성-관리?category=320582&lt;/a&gt;&lt;/p&gt;</description>
      <category>django</category>
      <category>Django</category>
      <category>requirements.txt</category>
      <category>의존성</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/142</guid>
      <comments>https://coding-lks.tistory.com/142#entry142comment</comments>
      <pubDate>Mon, 22 Feb 2021 22:36:51 +0900</pubDate>
    </item>
    <item>
      <title>[python] 가변 인자(*args, **kwargs)</title>
      <link>https://coding-lks.tistory.com/141</link>
      <description>&lt;h3&gt;*args(위치 가변 인자)&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;args는 *arguments의 줄임말입니다.&lt;/li&gt;
&lt;li&gt;임의의 개수의 인자를 받는 함수를 가리켜 가변 인자를 사용한다고 표현합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;그리고 무조건 *args라는 이름을 지키지 않아도 작동이 됩니다!&lt;/p&gt;
&lt;p&gt;&amp;rArr; a 라고 써도 되고, *asdfadfads라고 적어도 되고, 아래 예시 처럼 *Names라고 써도 정상작동합니다.&lt;/p&gt;
&lt;p&gt;이 지시어는 여러 개(복수개의)의 인자를 함수로 받고자 할 때 쓰입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2021-02-22 오후 10.06.21.png&quot; data-origin-width=&quot;1374&quot; data-origin-height=&quot;490&quot; width=&quot;666&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dqNQ0V/btqX6gyJWpL/F3CkEevpuhuHucRk62zIcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dqNQ0V/btqX6gyJWpL/F3CkEevpuhuHucRk62zIcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dqNQ0V/btqX6gyJWpL/F3CkEevpuhuHucRk62zIcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdqNQ0V%2FbtqX6gyJWpL%2FF3CkEevpuhuHucRk62zIcK%2Fimg.png&quot; data-filename=&quot;스크린샷 2021-02-22 오후 10.06.21.png&quot; data-origin-width=&quot;1374&quot; data-origin-height=&quot;490&quot; width=&quot;666&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이런식으로 *args에 들어온 값들을 튜플 형태로 처리합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;**kwargs(키워드 가변 인자)&lt;/h3&gt;
&lt;p&gt;함수는 임의의 개수의 키워드 인자도 받을 수 있습니다.&lt;/p&gt;
&lt;p&gt;**kwargs는 keyword argument의 줄임말로 키워드를 제공합니다.&lt;/p&gt;
&lt;p&gt;바로 예시를 볼까요?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2021-02-22 오후 10.06.52.png&quot; data-origin-width=&quot;1218&quot; data-origin-height=&quot;378&quot; width=&quot;712&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dBoMZQ/btqX6grYRSa/NfKENmxMn5ubz4P9r6m2K1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dBoMZQ/btqX6grYRSa/NfKENmxMn5ubz4P9r6m2K1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dBoMZQ/btqX6grYRSa/NfKENmxMn5ubz4P9r6m2K1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdBoMZQ%2FbtqX6grYRSa%2FNfKENmxMn5ubz4P9r6m2K1%2Fimg.png&quot; data-filename=&quot;스크린샷 2021-02-22 오후 10.06.52.png&quot; data-origin-width=&quot;1218&quot; data-origin-height=&quot;378&quot; width=&quot;712&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;*kwargs는 (key(키워드) = value(값)) 형태로 함수를 호출할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;추가적인 키워드를 딕셔너리 형태로 {key(키워드) = value(값)} 요렇게 함수 내부로 전달됩니다.&lt;/p&gt;
&lt;p&gt;그렇게 전달받은 딕셔너리를 마음대로 조리하면 되겠죠?&lt;/p&gt;
&lt;p&gt;이것을 또 다르게 사용하는 방법으로는&lt;/p&gt;
&lt;p&gt;특정 키워드에 반응하여 함수를 작성하는 방법이 있습니다.&lt;/p&gt;
&lt;p&gt;제 이름인 ant가 입력되었을 때는 다르게 반응하고 싶게 만들어봅시다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;*args, **kwargs를 둘 다 써보자.&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;둘 다 써보는 것은 어떨까요?&lt;/p&gt;
&lt;p&gt;위의 내용을 조금 변형해서 써봅시다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2021-02-22 오후 10.07.22.png&quot; data-origin-width=&quot;2252&quot; data-origin-height=&quot;1504&quot; width=&quot;754&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kG8vh/btqYiB194zF/jRbnD3grsKxBYqWBFE6nXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kG8vh/btqYiB194zF/jRbnD3grsKxBYqWBFE6nXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kG8vh/btqYiB194zF/jRbnD3grsKxBYqWBFE6nXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkG8vh%2FbtqYiB194zF%2FjRbnD3grsKxBYqWBFE6nXK%2Fimg.png&quot; data-filename=&quot;스크린샷 2021-02-22 오후 10.07.22.png&quot; data-origin-width=&quot;2252&quot; data-origin-height=&quot;1504&quot; width=&quot;754&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;함수명 바로 아래에 다중 문자열(''')을 넣어서 함수의 docstring으로 만들어 줍니다.&lt;/p&gt;
&lt;p&gt;저렇게 함수에다가 docstring을 넣으면 help()함수로 함수의 설명을 읽을 수 있습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;blogs에는 블로그1, 블로그2, 블로그3을 arguments로 넣었고,&lt;/li&gt;
&lt;li&gt;*blogs_benefits는 블로그수익1, 블로그수익2, 블로그수익3 으로 각각 키워드를 넣어서 입력했습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;보다시피 &lt;i&gt;blogs , *&lt;/i&gt;blogs_benefits를 순서대로 넣은 것을 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;순서를 바꾸면 어떻게 될까요?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2021-02-22 오후 10.07.54.png&quot; data-origin-width=&quot;2230&quot; data-origin-height=&quot;1208&quot; width=&quot;764&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c4SXgk/btqYech6kj8/zjqfCnP8l02PwMVNV99Py1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c4SXgk/btqYech6kj8/zjqfCnP8l02PwMVNV99Py1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c4SXgk/btqYech6kj8/zjqfCnP8l02PwMVNV99Py1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc4SXgk%2FbtqYech6kj8%2FzjqfCnP8l02PwMVNV99Py1%2Fimg.png&quot; data-filename=&quot;스크린샷 2021-02-22 오후 10.07.54.png&quot; data-origin-width=&quot;2230&quot; data-origin-height=&quot;1208&quot; width=&quot;764&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;작동하지 않습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이를 통해서 *&lt;i&gt;args, *&lt;/i&gt;kwargs도 순서가 있다는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt;var, &lt;i&gt;args, *&lt;/i&gt;kwargs 함수를 호출할 때 인수를 이 순서로 넣어줘야 정상작동한다!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;결론 및 요약&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;function(&lt;i&gt;args, *&lt;/i&gt;kwargs)&lt;/p&gt;
&lt;p&gt;return 0;&lt;/p&gt;
&lt;p&gt;함수의 파라미터 순서 : 일반 변수(var), &lt;i&gt;변수(*args&lt;/i&gt;), &lt;b&gt;변수(&lt;/b&gt;kwargs)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;변수 -&amp;gt; 여러개가 아규먼트로 들어올 때, 함수 내부에서는 해당 변수를 'tuple'로 처리한다.&lt;/li&gt;
&lt;li&gt;*변수 -&amp;gt; 키워드='' 로 입력할 경우에 그것을 각각 키와 값으로 가져오는 'dict'로 처리한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;충분히 복습하면서 연습해보는 것을 권장합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;참조:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://brunch.co.kr/@princox/180&quot;&gt;https://brunch.co.kr/@princox/180&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://wikidocs.net/84426&quot;&gt;https://wikidocs.net/84426&lt;/a&gt;&lt;/p&gt;</description>
      <category>python</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/141</guid>
      <comments>https://coding-lks.tistory.com/141#entry141comment</comments>
      <pubDate>Mon, 22 Feb 2021 22:09:39 +0900</pubDate>
    </item>
    <item>
      <title>[python] 이터레이터와 제너레이터</title>
      <link>https://coding-lks.tistory.com/140</link>
      <description>&lt;h3&gt;iterator?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;값을 순차적으로 꺼낼 수 있는 객체; value factory&lt;/li&gt;
&lt;li&gt;List, Dict, Set, Tuple, Str 등 순회 가능한 sequential 객체 = iterable object&lt;/li&gt;
&lt;li&gt;dir로 객체를 호출하여 __iter__함수가 있으면 이터레이터&lt;/li&gt;
&lt;li&gt;__next__() 메소드를 가진 모든 객체는 이터레이터 &amp;rarr; 다음요소를 하나씩 꺼내오는 함수&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;L = [1, 2, 3]
iterator_L = iter(L)
print(iterator_L.__next__())  #1 
print(iterator_L.__next__())  #2
print(iterator_L.__next__())  #3
print(iterator_L.__next__())  #StopIteration 에러&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Iterable vs Iterator&lt;/h3&gt;
&lt;p&gt;:이터러블은 이터레이터를 반환할 수 있는 모든 객체이다. (꼭 자료구조여야만 하는 것은 아니다.)&lt;/p&gt;
&lt;p&gt;이터러블한 객체를 iter()을 사용하여 이터레이터 객체를 반환한다.&lt;/p&gt;
&lt;p&gt;예시&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;x = [1, 2, 3]   #iterable 
y = iter(x)     #instance of an iterator 
print(next(y))
print(next(y))

print(type(x))
print(type(y))

결과:
1
2
&amp;lt;class 'list'&amp;gt;
&amp;lt;class 'list_iterator'&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;iter &amp;amp; next&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;iter는 객체의 __iter__ 함수를 호출하는 함수&lt;/li&gt;
&lt;li&gt;next는 객체의 __next__ 함수를 호출하는 함수&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;L = [1, 2, 3]

I = iter(L)
while True:
        try:
                x = next(I)
        except StopIteration:
                break
        print(x**2, end=&quot; &quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이터레이터는 size 확인하기가 불가능해서 데이터 길이 만큼만 while문을 실행하지 못했다.&lt;/p&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;D = {'a':1, 'b':2, 'c':3}
d_iterator = iter(D)
while True:
    d_next = next(d_iterator)
    print(d_next)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 예시처럼 StopIteration이 발생하는데&lt;/p&gt;
&lt;p&gt;이 오류가 발생하지 않도록 하는 방법이 뭘까???&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;예외처리문으로 StopIteration 이 발생하는 경우 무한반복을 탈출(break) 해주면 된다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;d_iterator = iter(D)
while True:
        try:
                d_next = next(d_iterator)
                print(d_next)
        except StopIteration: 
                break&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Generator?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;이터레이터를 생성하는 함수&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;모든 값을 포함하여 반환하는 대신 호출할 때마다 하나의 값을 리턴&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;따라서 작은 메모리로 효율적으로 대용량의 이터러블을 순회할 수 있다는 것이 큰 장점&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;dir로 확인해보면 __i&lt;i&gt;ter_&lt;/i&gt;_, __&lt;i&gt;next&lt;/i&gt;__ 함수다 모두 들어있음 따라서 __iter__를 호출할 필요 없이 바로 __next__함수가 모두 들어있음&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;yield : 제너레이터 함수에서 값을 반환할 때 사용되고 yield가 호출된 후 다음 next가 호출되기 전까지 hold하다가 next가 호출되면 이전 상태에 이어서 다음 연산 수행&lt;/p&gt;
&lt;p&gt;여기서 yield 값은 어떤 메소드를 사용했냐에 따라 다르다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;__next__() : None&lt;/li&gt;
&lt;li&gt;send() : 메소드로 전달된 값&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;예제1&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;def generator_send():
    received_value = 0
    while True:
        received_value = yield
        print('received_value: ', received_value)
        yield received_value**2 

gen = generator_send()
print(gen)  
# &amp;lt;generator object generator_send at 0x000001DEA95A63C0&amp;gt;

next(gen)
print(gen.send(2))
#received_value:  2
#4

next(gen)
print(gen.send(3))
#received_value:  3
#9&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;동작&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;yield문이 포함된 제너레이터 함수를 실행하면 제너레이터 객체가 반환되는데 이 때는 함수의 내용이 실행되지 않는다.(그저 변수에 함수를 담을뿐!) &amp;rarr; gen = generator_send() 구문 이야기 이다.&lt;/li&gt;
&lt;li&gt;next()라는 빌트인 메소드를 통해 제너레이터 함수를 실행시킬 수 있으며 next()메소드 내부적으로 iterator를 인자로 받아 이터레이터의 __next__() 메소드를 실행시킨다. &amp;rarr; next(gen) 구문&lt;/li&gt;
&lt;li&gt;처음 __next__() 메소드를 호출하면 함수의 내용을 실행하다 yield문을 만났을 때 처리를 중단한다.&lt;/li&gt;
&lt;li&gt;이 때 모든 local state는 유지되는데 변수의 상태, 명령어 포인터, 내부 스택, 예외 처리 상태를 포함한다.&lt;/li&gt;
&lt;li&gt;그 후 제어권을 상위 컨텍스트로 양보(yield)하고 또 __next__()가 호출되면 제너레이터는 중단된 시점부터 다시 시작한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그러니까 gen.send(2)를 실행하면 함수내부에서 received_value에는 2가 들어가고 2**2인 4를 반환한다.&lt;/p&gt;
&lt;p&gt;이상태 에서 다시 호출될때까지 제너레이터는 대기상태!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;예제2&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;def gen():
    print('gen start')
    data1 = yield 1 
    print(f'gen[1]: {data1}')
    data2 = yield 2
    print(f'gen[2]: {data2}')
    return 'done'
g = gen()

#data1 input -&amp;gt; 1st print, 1 출력 
first = g.send(None)
print(first)

#data2 input  -&amp;gt; 2nd print, 2 출력 ㅣㅔ
second = g.send('world')
print(second)

#data3 input -&amp;gt; 3rd print, StopItration: done 출력 (return 'done 을 yield 3 으로 바꾸면 3 출력 )
print(g.send('hello'))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Generator expression&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;리스트 컴프리핸션과 동일한 방식의 표현식&lt;/li&gt;
&lt;li&gt;소괄호를 사용하지만 tuple comprehension 이 아님!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt; 예시&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;lsl&quot;&gt;&lt;code&gt;numbers = [1, 2, 3, 4, 5, 6]

lazy_squares = (x * x for x in numbers)

&amp;gt;&amp;gt;&amp;gt; lazy_squares
&amp;lt;generator object &amp;lt;genexpr&amp;gt; at 0x10d1f5510&amp;gt;

&amp;gt;&amp;gt;&amp;gt; print(next(lazy_squares))
1
#❗️아래에서 list가 4부터 시작하는 이유는 위에서 next()메소드로 맨 처음 값을 이미 출력했기 때문❗️

&amp;gt;&amp;gt;&amp;gt; print(list(lazy_squares))
[4, 9, 16, 25, 36]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;rarr; def에 yield를 사용해서 또 Generator expression를 사용해서 generator객체를 만들어봤다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Generator의 효율성&lt;/h3&gt;
&lt;p&gt;사실 결과만이 목적이면 이런 어려운 개념을 공부할 필요없이 일반 list comprehesion를 쓰거나, 함수를 만들어쓰면 같은 결과를 구할 수 있다.&lt;/p&gt;
&lt;p&gt;우리의 랜덤 정수 예제도 그냥 list로 반환해서 써도 어지간한 요구사항은 똑같이 만족시킬 것이다.&lt;/p&gt;
&lt;p&gt;그럼에도 불구하고 어떤 추가적인 장점이 있기에 generator나 iterator을 쓰면 좋은 것일까???&lt;/p&gt;
&lt;p&gt;밑에서 알아보겠다!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;- Lazy evaluation을 통한 메모리 안전성&lt;/h4&gt;
&lt;p&gt;이 구절의 정확한 의미를 이해하기 위해 for문에서 같은 결과를 내는 list comprehension과 generator를 비교해서 살펴볼 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;for문을 통해 1부터 10까지 출력하고 싶다고하자.&lt;/p&gt;
&lt;p&gt;이때 in 키워드 뒤에 다음과 같은 list와 generator을 각각 둬서 같은 결과를 낼 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1613821748776&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for e in [n for n in range(1,11)]:
		print(e)

&amp;gt;&amp;gt;&amp;gt;
1
2
3
...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1613821787738&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for e in (n for n in range(1, 11)):
		print(e)

&amp;gt;&amp;gt;&amp;gt;
1
2
3
...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 두 코드는 정확히 같은 결과를 출력하면서 식도 비슷하다.&lt;/p&gt;
&lt;p&gt;차이라면 식을 완성하기 위해 [], ()의 사용유무 차이이다.&lt;/p&gt;
&lt;p&gt;첫번째 에서는 list가 반환돼어 쓰였고, 두번째에서는 Generator가 반환돼 쓰였다.&lt;/p&gt;
&lt;p&gt;하지만 이 둘에는 큰 차이가 있는데 내부적으로 컴퓨터 메모리와 관련이 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;먼저 리스트를 만들었던 첫 번째 예를 살펴보자. 리스트를 선언하면 하드웨어적으로 메모리에 그 리스트를 위한 공간이 할당된다.&lt;/p&gt;
&lt;p&gt;그것을 그림으로 표현하면 다음과 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7Gh3L/btqXXjWUn88/caJKDmiNKf4QR9AUkEQ1gk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7Gh3L/btqXXjWUn88/caJKDmiNKf4QR9AUkEQ1gk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7Gh3L/btqXXjWUn88/caJKDmiNKf4QR9AUkEQ1gk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7Gh3L%2FbtqXXjWUn88%2FcaJKDmiNKf4QR9AUkEQ1gk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;C 같은 언어에서는 일반적으로 정수 배열이 메모리 상에 연속적으로, 단절 없이 할당된다.&lt;/p&gt;
&lt;p&gt;그리고 여기서 쓰인 정수는 int형으로 4바이트를 차지한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위 그림에서 각 셀이 배열(array)의 한 원소를 나타내고 셀의 바로 밑에 있는 숫자는 각 원소가 차지하는 메모리의 위치 주소를 의미한다. 또한 주소 숫자 간격이 4라는 것을 알 수 있다.&lt;/p&gt;
&lt;p&gt;파이썬에서 list가 c에서의 array와는 구현이 조금 다르지만&lt;/p&gt;
&lt;p&gt;중요한 것은 1번과 같이 list comprehension 식을 입력했을 때 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;메모리에 배열의 크기에 비례하는 공간이 바로 할당된다&lt;/span&gt;는 것이다.&lt;/p&gt;
&lt;p&gt;위의 예에서라면 10개의 정수가 배열에 있으므로 이 배열의 총 크기는 40 byte(4 * 10)이 될 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;하지만 두 번째 에서는 다르다. generator expression을 통해 생성한 generator는 숫자 10개를 생성할 예정이지만 그것을 배열 등의 구체적인 형태로 가지고 있지 않다.&lt;/p&gt;
&lt;p&gt;다시 말해&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;generator expression은 지정한 규칙대로 값을 반환할 규칙과 현재 어디까지 반환했는지 들을 관리할 여러 상태 값을 담고 있지만 배열과 달리 값 모두를 generator를 생성할 당시에 메모리에 할당하지 않는다는 결정적인 차이가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이게 첫 번째 예제로 만든 list와의 큰 차이로서 이런 형태의 디자인 패턴을 lazy loading이라고 한다.&lt;/p&gt;
&lt;p&gt;이 lazy loading라는 패턴은 생각 보다 흔히 쓰이기 때문에 기억하고 있으면 좋다.&lt;/p&gt;
&lt;p&gt;반대로 첫 번째 예제처럼 생성과 동시에 메모리에 적재하는 패턴은 eager loading라고도 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;다음과 같은 질문을 던질 수 있다. 왜 굳이 이 둘을 분리해서 쓰는가? 결국 generator(iterator)을 통해 할 수 있는 모든 일은 list comprehension으로 잘할 수 있는데 말이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 존재의 이유를 보여드리겠습니다.&lt;/p&gt;
&lt;p&gt;만약 list의 크기가 10이 아니라 아주아주 큰값이라면 어떨까요?&lt;/p&gt;
&lt;pre id=&quot;code_1613821910560&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;big_list = [i for i in range(1, 100000000000000000000+1)]

# 생성이 안 됨&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아까와 달리 크기가 매우 큰 list를 생성하는 list comprehension이다.&lt;/p&gt;
&lt;p&gt;range함수 내의 두 번째 인자가 정확히 몇인지 눈으로 셀수는 없을 정도다.&lt;/p&gt;
&lt;p&gt;저 한 줄을 복사해서 파이썬 인터프리터에 실행시켜보라. 결과가 어떻게 나올까??&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아마 멈추거나 인터프리터가 종료되거나 할 것이다.&lt;/p&gt;
&lt;p&gt;그 이유는 list와 같은 자료구조는 eager loading으로 원소의 크기에 따라 총 크기가 결정되므로 저렇게 큰 크기의 list를 선언하면, 일반 컴퓨터의 한정된 용령의 메모리로는 감당할 수 없기 때문이다.&lt;/p&gt;
&lt;p&gt;메모리의 크기가 가상 메모리를 사용한다고 해도 일반 컴퓨터는 어지간하면 100기가바이트를 넘지 않을텐데 그것을 상회하도록 크기를 크게 만든 자료구조는 생성할 수 없다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;하지만 저렇게 큰 크기의 값을 생성하는 generator은 문제없이 실행된다.&lt;/p&gt;
&lt;pre id=&quot;code_1613821936848&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;big_gen = (i for i in range(1, 10000000000000000000000000000+1))

# 문제없이 바로 생성됨&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;심지어 지금 만든 generator은 앞선 big_list보다 생성할 값의 크기가 훨씬 더 큽니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이렇게 매우 큰 크기를 갖는 자료구조를 다룰 일이 현업에서는 생각보다 더 흔하다고 합니다.&lt;/p&gt;
&lt;p&gt;매우 큰 파일을 읽는다거나, 네트워크를 통해 대용량의 데이터를 다운로드할 때를 생각해보면, 줄 등의 한 단위를 필요할 때 한 줄씩 평가하는 (evalute) lazy loading이 한번에 모든 데이터를 loading하는 방식보다 안전한 것은 당연합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;정리하자면, iterable, iterator, generator의 장점은 일련의 값을 사용할 때 모든 값을 메모리에 모두 로딩하는 대신 한 값씩 필요할 때마다 로딩, 평가함으로써(lazy loading, evaluate)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;b&gt;메모리를 효율적&lt;/b&gt;으로 사용할 수 있다.
&lt;ul&gt;
&lt;li&gt;list 는 리스트 안의 모든 데이터를 메모리에 저장하기 때문에 list 의 크기만큼 메모리 용량을 사용하게 됩니다..&lt;/li&gt;
&lt;li&gt;반면에 generator 는 데이터 값을 모두 저장하는 것이 아니고 next() 메소드를 통해 값에 접근할 때마다 메모리에 저장하는 방식이어서 메모리 효율이 좋다는점.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Lazy evaluation&lt;/b&gt;( 말그대로 실행을 지연시킨다는 의미)을 통해 메모리 부족으로 프로그램이 실패하는 것을 방지함으로써 보장되는 안정성!!.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;참조:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://offbyone.tistory.com/83&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;offbyone.tistory.com/83&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://hackersstudy.tistory.com/9&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;hackersstudy.tistory.com/9&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://nirsa.tistory.com/118&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;nirsa.tistory.com/118&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://velog.io/@wltjs10645/Python-%EC%9D%B4%ED%84%B0%EB%A0%88%EC%9D%B4%ED%84%B0-%EC%A0%9C%EB%84%88%EB%A0%88%EC%9D%B4%ED%84%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;velog.io/@wltjs10645/Python-%EC%9D%B4%ED%84%B0%EB%A0%88%EC%9D%B4%ED%84%B0-%EC%A0%9C%EB%84%88%EB%A0%88%EC%9D%B4%ED%84%B0&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://mingrammer.com/translation-iterators-vs-generators/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;mingrammer.com/translation-iterators-vs-generators/&lt;/a&gt;&lt;/p&gt;</description>
      <category>python</category>
      <category>Generator</category>
      <category>iterable</category>
      <category>iterator</category>
      <category>PYTHON</category>
      <category>이터레이터</category>
      <category>제너레이터</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/140</guid>
      <comments>https://coding-lks.tistory.com/140#entry140comment</comments>
      <pubDate>Sat, 20 Feb 2021 00:20:29 +0900</pubDate>
    </item>
    <item>
      <title>[python] lambda expression(람다 표현식)</title>
      <link>https://coding-lks.tistory.com/139</link>
      <description>&lt;p&gt;람다 표현식으로 익명 함수를 만드는 방법을 알아보겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;람다 표현식은 식 형태로 되어 있다고 해서 람다 표현식(lambda expression)이라고 부릅니다&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;특히 람다 표현식은 함수를 간편하게 작성할 수 있어서 다른 함수의 인수로 넣을 때 주로 사용합니다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;특히 map, filter, reduce와 같이 매개변수로 함수가 필요한 곳에 유용하게 사용됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;사용시 기본 형식&lt;/p&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;lambda 인자:표현식&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;ex) 두 수를 더하는 함수&lt;/p&gt;
&lt;pre class=&quot;ruby&quot;&gt;&lt;code&gt;def hap(x, y)
    return x + y

hap(10, 20)

&amp;gt;&amp;gt;&amp;gt; 30&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위를 람다식으로 표현하면&lt;/p&gt;
&lt;pre class=&quot;gml&quot;&gt;&lt;code&gt;(lambda x,y:x+y)(10,20)

&amp;gt;&amp;gt;&amp;gt; 30&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;몇가지 함수를 더 배워보며 람다가 어떻게 이용되는지 알아보겠습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;람다를 변수에 할당하여 재사용 할 수도 있습니다.&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;lambdaAdd = lambda n,m: n + m

print(lambdaAdd(2, 3))
# 5

print(lambdaAdd(4, 5))
# 9&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;람다를 반환하는 함수를 작성해보기&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;def inc(n):
    return lambda x: x + n

f = inc(2)
g = inc(4)
print(f(12))
print(g(12))
print(inc(2)(12))

# 출력결과 : 
14
16
14&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;람다식 안에서 조건문도 사용 가능합니다.&lt;/h3&gt;
&lt;p&gt;ex) 매개변수로 주어진 값중 n이 짝수이면 n을 아니면 m을 반환하는 함수&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;print((lambda n, m: n if n%2==0 else m)(1, 3))
# 3

print((lambda n, m: n if n%2==0 else m)(2, 3))
# 2&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;람다 표현식에서 조건식 사용 시 기본 형식&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;lambda n, m: n if n%2==0 else m)(2, 3)

lambda 변수1, 변수2 : '참일때 반환할 값' if 구문 else '거짓일시 반환할 값'(변수1, 변수2)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;매개변수로 함수가 필요한 함수 중 대표적으로 map(), filter(), reduce()를 람다와 함께 사용해보겠습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;map()&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;먼저 map함수입니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;map(함수, 리스트)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 함수는 함수와 리스트를 인자로 받습니다.&lt;/p&gt;
&lt;p&gt;그리고, 리스트로부터 원소를 하나씩 꺼내서 함수를 적용시킨 다음, 그 결과를 새로운 리스트에 담아줍니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;map(lambda x: x ** 2, range(5))      # 파이썬 2
[0, 1, 4, 9, 16]

list(map(lambda x: x**2, range(5)))  # 파이썬2 및 파이썬3
[0, 1, 4, 9, 16]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 map 함수가 인자로 받은 함수는 lambda x:x**2구요,&lt;/p&gt;
&lt;p&gt;리스트로는 range(5)를 받습니다. range(5)는 [0, 1, 2, 3, 4]라는 리스트를 돌려줍니다.&lt;/p&gt;
&lt;p&gt;그리고 x**2라는 것은 x값을 제곱하라는 연산자 입니다.&lt;/p&gt;
&lt;p&gt;결국 위 예제에서는 [0, 1, 2, 3, 4]에서 각 원소를 제곱시킨 리스트를 반환하게됩니다!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;reduce()&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;리스트의 값을 순서대로 누적시켜가며 람다식을 적용한다.&lt;/p&gt;
&lt;p&gt;이번엔 reduce 함수를 살펴봅시다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;reduce(함수, 순서형 자료)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;형식은 위와 같구요, 순서형 자료(문자열, 리스트, 튜플)의 원소들을 함수를 적용시켜 누적시킵니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;from functools import reduce

reduce(lambda x, y: x + y, [0, 1, 2, 3, 4])
&amp;gt;&amp;gt;&amp;gt;10&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 예제에서는 먼저 0과 1을 더하고, 그 결과에 2를 더하고, 거기에 3을더하고, 또 4를 더한 값을 돌려줍니다.&lt;/p&gt;
&lt;p&gt;한 마디로 전부 다 더하라는 겁니다. 생각보다 쉽죠?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;gml&quot;&gt;&lt;code&gt;reduce(lambda x, y: y + x, 'abcde')
&amp;gt;&amp;gt;&amp;gt;'edcba'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;y+x 구문이 동작하므로&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1. b+a&lt;/p&gt;
&lt;p&gt;2. c+ba&lt;/p&gt;
&lt;p&gt;... 'edcba'가 반환됩니다!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;filter()&lt;/h3&gt;
&lt;p&gt;지정한 조건을 만족시키는 값들로만 리스트를 구성하여 반환&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;filter(함수, 리스트)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;필터함수는 이렇게 생겼는데요.&lt;/p&gt;
&lt;p&gt;리스트에 들어있는 원소들을 함수에 적용시켜서 결과가 참인 값들로 새로운 리스트를 만들어 줍니다&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;다음은 0부터 9까지의 리스트 중에서 5보다 작은 것만 돌려주는 예제입니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;filter(lambda x: x &amp;lt; 5, range(10))    #파이썬2
&amp;gt;&amp;gt;&amp;gt; [0, 1, 2, 3, 4]

list(filter(lambda x: x &amp;lt; 5, range(10))) # 파이썬2, 3
&amp;gt;&amp;gt;&amp;gt; [0, 1, 2, 3, 4]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;5미만인 값만 살려서 리스트로 반환한다!&lt;/p&gt;
&lt;p&gt;x &amp;lt; 5 가 참이면 값을 살리는 구문!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;홀수로만 이루어진 리스트를 반환하는 필터를 만들어보겠습니다&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;filter(lambda x : x % 2, range(10)) #파이썬 2
[1, 3, 5, 7, 9]

list(filter(lambda x : x % 2, range(10))) # 파이썬 2,3
[1, 3, 5, 7, 9]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;1은 참 0은 거짓!&lt;/p&gt;
&lt;p&gt;이므로&lt;/p&gt;
&lt;p&gt;x%2가 1이면 리스트에 추가&lt;/p&gt;
&lt;p&gt;0이면 걸러낸다고 생각하시면 됩니다!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;마지막으로 한번더 map, reduce, filter를 람다함수를 이용하여 작성해보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;from functools import reduce


l = list(range(1, 11))
print(l)
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

m = list(map(lambda n:n*n, l))
print(m)
# [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

r = reduce(lambda n,m:n*m, l)
print(r)
# 362880

f = list(filter(lambda n:n%2==0, l))
print(f)
# [2, 4, 6, 8, 10]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;람다함수는 특히 map, filter, reduce와 같이 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;매개변수로 함수가 필요한 곳에 유용하게 쓰입니다&lt;/span&gt;!&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;참조:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://dpdpwl.tistory.com/87&quot;&gt;https://dpdpwl.tistory.com/87&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://offbyone.tistory.com/73&quot;&gt;https://offbyone.tistory.com/73&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://dojang.io/mod/page/view.php?id=2359&quot;&gt;https://dojang.io/mod/page/view.php?id=2359&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://wikidocs.net/64&quot;&gt;https://wikidocs.net/64&lt;/a&gt;&lt;/p&gt;</description>
      <category>python</category>
      <category>Lambda</category>
      <category>lambda expression</category>
      <category>PYTHON</category>
      <category>람다</category>
      <category>람다표현식</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/139</guid>
      <comments>https://coding-lks.tistory.com/139#entry139comment</comments>
      <pubDate>Fri, 19 Feb 2021 17:16:40 +0900</pubDate>
    </item>
    <item>
      <title>[python] Decorator(데코레이터)</title>
      <link>https://coding-lks.tistory.com/138</link>
      <description>&lt;h2&gt;Decorate 개념&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;decorator : 실내장식가&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;데커레이터(Decorator)는 하나의 함수를 취해서 또 다른 함수를 반환하는 함수입니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;@decorator
def function():
    print &quot;what is decorator?&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;한마디로 얘기하자면, 대상 함수를 wrapping 하고, 이 wrapping 된 함수의 앞뒤에 추가적으로 꾸며질 구문 들을 정의해서 손쉽게 재사용 가능하게 해주는 것입니다....?(자세한 설명은 밑에서)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Decorator는 어떤 경우에 씁니까?&lt;/h3&gt;
&lt;p&gt;코딩을 하다 보면 종종 이런 경우가 있습니다.&lt;/p&gt;
&lt;p&gt;한 구문이 있고, 여기에 부가적인 구문을 추가하고 싶을때가 있습니다.&lt;/p&gt;
&lt;p&gt;그리고 이 부가적인 구문을 반복해서 사용하고 싶은 경우도 있습니다.&lt;/p&gt;
&lt;p&gt;이때 부가적인(그리고 반복적인) 작업을 decorator 로 선언해서 자유롭게 사용이 가능합니다.&lt;/p&gt;
&lt;p&gt;처음엔 이해가 잘 안가지만, 막상 사용하다 보면 굉장히 쉽다는 것을 느낄 수 있습니다.&lt;/p&gt;
&lt;p&gt;아래에서 간단한 예시와 함께 데코레이터 사용 시의 장점을 알아보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;def main_function():
    print &quot;MAIN FUNCTION START&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;만약 해당 함수가 출력되기 전과 후에 날짜와 시간을 출력해야 한다고 합시다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;import datetime

def main_function():
     print datetime.datetime.now()
     print &quot;MAIN FUNCTION START&quot;
     print datetime.datetime.now()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위와 같이 작성할 수 있습니다.&lt;/p&gt;
&lt;p&gt;하지만 위와 같은 작업을 여러번 반복해야 한다면?&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import datetime

def main_function_1():
     print datetime.datetime.now()
     print &quot;MAIN FUNCTION 1 START&quot;
     print datetime.datetime.now()

def main_function_2():
     print datetime.datetime.now()
     print &quot;MAIN FUNCTION 2 START&quot;
     print datetime.datetime.now()

def main_function_3():
     print datetime.datetime.now()
     print &quot;MAIN FUNCTION 3 START&quot;
     print datetime.datetime.now()

.... X 100번&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;반복되는 구문이 많다보니 소스가 지저분해져 가독성이 떨어집니다.&lt;/p&gt;
&lt;p&gt;함수 실행 전과 후에 시간을 출력하는 간단한 구문이 이런데 실제 사용될 때는 소스의 상태가 심각해질 것입니다.&lt;/p&gt;
&lt;p&gt;이럴때 아래와 같이 데코레이터를 사용할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;import datetime

def datetime_decorator(func):
        def decorated():
                print datetime.datetime.now()
                func()
                print datetime.datetime.now()
        return decorated

@datetime_decorator
def main_function_1():
        print &quot;MAIN FUNCTION 1 START&quot;

@datetime_decorator
def main_function_2():
        print &quot;MAIN FUNCTION 2 START&quot;

@datetime_decorator
def main_function_3():
        print &quot;MAIN FUNCTION 3 START&quot;

..... X 100번&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;decorator 함수를 재사용 함으로써, main 함수에 대한 가독성과 직관성이 훨씬 좋아진 것을 볼 수 있습니다다.&lt;/p&gt;
&lt;p&gt;그리고 같은 패턴을 여러번 사용하더라고 간단히 @를 붙이면 끝이므로 사용도 간편합니다.&lt;/p&gt;
&lt;p&gt;아래에서는 데코레이터의 사용 예시와 함께 설명해보겠습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;데코레이터 예제1 : 수동으로 활용하기&lt;/h3&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;# 데코레이터 예제 

def decorator_function(original_function): #1, #4 
    def wrapper_function(): #5 #8 
        return original_function() #9 
    return wrapper_function #6 

def display(): #2, #10 
    print(&quot;display 함수가 실행됐습니다&quot;) #11 

decorated_display = decorator_function(display)  #3 display 함수를 전달 
decorated_display() #7 

출력결과 :

display 함수가 실행됐습니다

display 함수가 실행됐습니다
None
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위 코드는 &lt;code&gt;#&lt;/code&gt;의 순서를 따른다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;#3&lt;/code&gt;: decorated_display라는 변수는 decorated_function 함수의 리턴값을 할당받는다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;#6&lt;/code&gt;: return값은 wrapper_function이라는 함수다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;#3&lt;/code&gt;변수는 결국 함수를 실행시킬 수 있는 함수다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;#7&lt;/code&gt;: 변수가 함수니까 괄호를 붙여 실행시켜보자.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;#8&lt;/code&gt;, &lt;code&gt;#9&lt;/code&gt; : return 값으로 original_function을 돌려준다.
&lt;ul&gt;
&lt;li&gt;여기서 closure 함수의 개념이 쓰인다.&lt;/li&gt;
&lt;li&gt;바깥 함수(decorator_function)에서 전달받은 &lt;code&gt;original_function&lt;/code&gt;을 기억하고 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;#10&lt;/code&gt;, &lt;code&gt;#11&lt;/code&gt; : 여기서 비로소 함수를 실행하고, print문을 실행한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;display함수에서 반환값이 없기 때문에 함수 자체의 반환값은 None입니다!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;# 데코레이터 예제 

def decorator_function(original_function):
    def wrapper_function():
        print(&quot;{} 함수가 호출되기 전입니다.&quot;.format(original_function.__name__))
        return original_function()
    return wrapper_function

def display_1():
    print(&quot;display_1 함수가 실행됐습니다.&quot;)

def display_2():
    print(&quot;display_2 함수가 실행됐습니다.&quot;)

display_1 = decorator_function(display_1)
display_2 = decorator_function(display_2)

display_1()
print(&quot;&quot;)
display_2()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;wrapper 함수를 통해서 간단하게 기능 추가를 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;데코레이터 예제2 : &lt;code&gt;@&lt;/code&gt; 심볼 활용하기&lt;/h3&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;# @ 심볼 활용하기 

def decorator_function(original_function):
    def wrapper_function():
        print(&quot;{} 함수가 호출되기 전입니다.&quot;.format(original_function.__name__))
        return original_function()
    return wrapper_function

@decorator_function
def display_1():
    print(&quot;display_1 함수가 실행됐습니다.&quot;)

@decorator_function
def display_2():
    print(&quot;display_2 함수가 실행됐습니다.&quot;)

# display_1 = decorator_function(display_1) 
# display_2 = decorator_function(display_2) 

출력 결과:

display_1() # 변수에 함수형 리턴값 할당 없이 바로 함수 호출이 가능하다. 
display_2() # 변수에 함수형 리턴값 할당 없이 바로 함수 호출이 가능하다. 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;데코레이터 예제3 : 인자가 전달되는 함수는 어떻게 데코레이팅 할까?&lt;/h3&gt;
&lt;h3&gt;예제3.1 : 위치인자와 키워드인자 활용하기&lt;/h3&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;def decorator_function(original_function):
    def wrapper_function():
        print(&quot;{} 함수가 호출되기 전입니다.&quot;.format(original_function.__name__))
        return original_function()
    return wrapper_function

@decorator_function
def display_1():
    print(&quot;display_1 함수가 실행됐습니다.&quot;)

@decorator_function
def display_info(name, age):  # 위 예제와 다르게 인자가 전달된다.
    print(&quot;display_info( {}, {} ) 함수가 실행됐습니다.&quot;.format(name, age))

# display_1 = decorator_function(display_1) 
# display_2 = decorator_function(display_2) 

display_1()
display_info('김아무개', 37)

출력 결과:

 TypeError: wrapper_function() takes 0 positional arguments but 2 were given

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;display_1 함수는 정상적으로 출력되지만, &lt;code&gt;display_info&lt;/code&gt; 함수는 타입에러가 생겼습니다.&lt;/p&gt;
&lt;p&gt;wrapper 함수에 위치인자와 키워드인자를 넣어주면 해결됩니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;def decorator_function(original_function):
    def wrapper_function(*args, **kwargs):
        print(&quot;{} 함수가 호출되기 전입니다.&quot;.format(original_function.__name__))
        return original_function(*args, **kwargs)
    return wrapper_function
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;예제3.2 : 위치인자와 키워드인자 활용하기&lt;/h3&gt;
&lt;p&gt;다음 예제는 &lt;code&gt;Introducing Python&lt;/code&gt;이라는 책에서 발췌한 예제이다.&lt;/p&gt;
&lt;p&gt;여기서는 위치인자와 키워드인자까지 활용했습니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;def document_it(func):
    def new_function(*args, **kwargs):
        print('Running function : ', func.__name__)
        print('Positional arguments : ', args)
        print('Keyword arugments :' , kwargs)
        result = func(*args, **kwargs)
        print('Result : ', result)
        return result
    return new_function

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


def div_inits(a,b):
    return a / b

add = document_it(add_inits)
div = document_it(div_inits)

print(add(5,3))
print(&quot;&quot;)
print(div(5,3))


출력 결과:

Running function :  add_inits
Positional arguments :  (5, 3)
Keyword arugments : {}
Result :  8
8

Running function :  div_inits
Positional arguments :  (5, 3)
Keyword arugments : {}
Result :  1.6666666666666667
1.6666666666666667
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;데코레이터 예제4 : 인자가 전달되는 함수는 어떻게 데코레이팅 할까?&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;데코레이터에서 파라미터를 입력받고 싶다면, 조금 복잡한데 함수를 2개로 만들어서 이용&lt;/b&gt;합니다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;반환값인 return을 잘 맞추어서 사용&lt;/b&gt;해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;from functools import wraps
        def print_a(file_name):
                def decorate(func):
                @wraps(func)
                def wrapper(*args, **kwargs):
                        print(file_name)
                        return func(*args, **kwargs)
                return wrapper
        return decorate

@print_a(&quot;hahaha&quot;)  #데코레이터에 &quot;hahaha&quot;를 입력받았다!
def print_c():
        print(&quot;c&quot;)

print_c()

결과:

hahaha
c&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;데코레이터 예제5 : &lt;b&gt;class 형태로 decorator를 사용해 보기.&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;decorator를 class로 사용하고 싶다면 아래와 같이 &lt;b&gt;call&lt;/b&gt; 함수로 decorator 형식을 정의해 주면됩니다.&lt;/p&gt;
&lt;p&gt;class의 &lt;b&gt;call&lt;/b&gt; 함수로 정의해주는게 nested(중첩) 함수 형식으로 정의한 것 보다 더 깔끔해 보입니다.&lt;/p&gt;
&lt;pre class=&quot;python&quot;&gt;&lt;code&gt;import datetime

class DatetimeDecorator:
        def __init__(self, f):
                self.func = f
        def __call__(self, *args, **kwargs):
                print(datetime.datetime.now())
                self.func(*args, **kwargs)
                print(datetime.datetime.now())
                print('\n')

class MainClass:
        @DatetimeDecorator
        def main_function_1():
                print(&quot;MAIN FUNCTION 1 START&quot;)

        @DatetimeDecorator
        def main_function_2():
                print(&quot;MAIN FUNCTION 2 START&quot;)

        @DatetimeDecorator
        def main_function_3():
                print(&quot;MAIN FUNCTION 3 START&quot;)

my = MainClass()

my.main_function_1()
my.main_function_2()
my.main_function_3()&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;그래서, 데코레이터를 언제 쓰는데?&lt;/b&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;로그를 남길 때&lt;/li&gt;
&lt;li&gt;유저의 로그인 상태를 확인하여 로그인 페이지로 리다이렉트(redirect)&lt;/li&gt;
&lt;li&gt;프로그램 성능을 위한 테스트&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이렇게 파이썬 데코레이터란 무엇이고, 어떻게 사용해야 하는지에 대해서 알아보았습니다.&lt;/p&gt;
&lt;p&gt;안에 wrapper함수 뒤에 코드를 정의하면, 함수 실행을 마치고 실행되야 하는 코드도 정의할 수 있습니다.&lt;/p&gt;
&lt;p&gt;데코레이터의 활용범위는 생각보다 넓을 듯 합니다.&lt;/p&gt;
&lt;p&gt;또한 함수별로 필수적으로 실행해야 하는 기능을 한 번에 정의할 수 있어 편리하고, 특히 유지보수 하기에 용이합니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그리고 이건 파이썬 데코레이터를 작성하는 방법을 배워야하는 이유 5가지라는 제목의 글이다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.hanbit.co.kr/media/channel/view.html?cms_code=CMS5689111564&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.hanbit.co.kr/media/channel/view.html?cms_code=CMS5689111564&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;참조:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://whatisthenext.tistory.com/113&quot;&gt;https://whatisthenext.tistory.com/113&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://tariat.tistory.com/846&quot;&gt;https://tariat.tistory.com/846&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://bluese05.tistory.com/30&quot;&gt;https://bluese05.tistory.com/30&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://schoolofweb.net/blog/posts/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EB%8D%B0%EC%BD%94%EB%A0%88%EC%9D%B4%ED%84%B0-decorator/&quot;&gt;http://schoolofweb.net/blog/posts/파이썬-데코레이터-decorator/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>python</category>
      <category>closure</category>
      <category>decorator</category>
      <category>PYTHON</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/138</guid>
      <comments>https://coding-lks.tistory.com/138#entry138comment</comments>
      <pubDate>Thu, 18 Feb 2021 20:30:09 +0900</pubDate>
    </item>
    <item>
      <title>[python] Closure(클로저 )</title>
      <link>https://coding-lks.tistory.com/137</link>
      <description>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;파이썬의 클로져 에 대해 알아본다&lt;/p&gt;
&lt;p&gt;클로져의 영단어 뜻은 '폐쇄'라는 뜻이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2. 배경지식&lt;/h3&gt;
&lt;p&gt;먼저 closure에 대해 바로 들어가기 전에 배경지식을 살펴본다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;함수의 중첩, 일급 객체, 파이썬의 nonlocal 스코프&lt;/span&gt;에 대해 알아보겠다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.1 함수 중첩&lt;/h3&gt;
&lt;p&gt;python에서 if 조건문 등은 중첩이 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1613568778048&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;n = int(input(&quot;정수를 입력하세요&quot;))

if n % 2 == 0:
    if n % 3 == 0:
        print(&quot;6의 배수군요?&quot;)
    else:
        print(&quot;짝수로소이다 ㅎㅎ&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;인터프리터에 'import this'라고 입력하면 유명한 &amp;lsquo;Zen of Python&amp;lsquo;[파이썬 계명]에서는 가급적 중첩을 피하라고 이야기하고 있다. 하지만 중첩을 해야만 하는 경우도 있기 때문에 중첩이 틀린 건 아니다.&lt;/p&gt;
&lt;p&gt;그리고 파이썬에서는 함수도 중첩 선언이 된다 그러니까 함수 정의 안에 다른 함수를 정릐할 수 있는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1613568797431&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def greetings():
    def say_hi():
        print(&quot;Hi, everyone :)&quot;)

    say_hi()


&amp;gt;&amp;gt;&amp;gt; greetings()
    
Hi, everyone :)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;인사를 건네는 greetings함수를 만들었다. 이 함수는 실제 인사를 건네는 코드를 다른 함수로 감사서 이 내부 함수를 호출하고 있다. 이 코드는 전혀 문제가 없다.&lt;/p&gt;
&lt;p&gt;이러헤 내부 함수를 짜면 이게 다 Closure인줄 알았다. 하지만 그건 틀린 생각이었고, &lt;span style=&quot;background-color: #9feec3;&quot;&gt;함수 중첩은 Closure이기 위한 필요조건이지 충분조건이 아니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.2 First Class Object&lt;/h3&gt;
&lt;p&gt;좀 생소한 개념이 나왔다. 그런데 생각보다 어렵지는 않다.&lt;/p&gt;
&lt;p&gt;프로그래밍 언어에서 First Class Object(또는 First Class Citizen, 일급 객체)는 해당 언어 내에서 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;일반적으로 다른 모든 개체에 통용가능한 동작(Operation)이 지원되는 개체(entity)를 의미&lt;/span&gt;한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;일급 객체(first-class object)란 다음 조건을 만족하는 객체를 뜻합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;변수나 데이터 구조에 넣을 수 있어야 한다.&lt;/li&gt;
&lt;li&gt;매개변수에 전달할 수 있어야 한다.&lt;/li&gt;
&lt;li&gt;반환값으로 사용할 수 있어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;특히 일급 함수(first-class function)는 일급 객체의 조건을 만족하면서 실행 중(run-time)에 함수를 생성할 수 있어야 합니다. 파이썬에서는 def 안에서 def로 함수를 만들거나, lambda를 사용하여 실행 중에 함수를 생성할 수 있으므로 파이썬의 함수는 일급 함수입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 동작은 주로&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;함수의 인자로 전달되거나,&lt;/li&gt;
&lt;li&gt;함수의 반환값이 되거나,&lt;/li&gt;
&lt;li&gt;수정되고 할당되는 것들을 전제로 한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이게 무슨 뜻일까? 어렵지 않다. 파이썬에서 가장 많이 쓰이는 자료타입이 뭐가 있을까? list, str, int 등등이 쉽게 떠오란다.&lt;/p&gt;
&lt;p&gt;이렇게 기본적이고 유명한 자료타입들은 함수의 인자로 전달되거나, 반환값이 되거나, 수정되고 할당될 수 있다.(적어도 개념적으로는)&lt;/p&gt;
&lt;p&gt;이런 자료형은 파이썬의 1급 객체다.&lt;/p&gt;
&lt;p&gt;반례) c언어세너는 함수의 인자로 함수의 포인터를전달할 수는 있어도, 함수의 이름을 전달할 수는 없다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;기술적으로 포인터가 일급 객체라는 표현은 맞아도 함수가 일급객체라는 표현은 적절하지 않은 것이다.&lt;/p&gt;
&lt;p&gt;반면 파이썬에서는 함수도 1급객체다.&lt;/p&gt;
&lt;p&gt;함수에는 위의 기본 자료타입들에 적용가능한 동작들이 똑같이 적용가능하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1613568819686&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def add(a, b):
    return a + b

def execute(func, *args):
    return func(*args) # 2.

f = add # 3.

&amp;gt;&amp;gt;&amp;gt; execute(f, 3, 5) # 1.

8&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Packing와 Unpacking을 활용한 간단한 예제이다.&lt;/p&gt;
&lt;p&gt;두수의 합을 반환하는 함수 add를 정의 했는데 이를 직접 호출하지 않고 감싸는 execute함수를 만들었다.&lt;/p&gt;
&lt;p&gt;이 예제에서 앞서 이야기한 1급 객체에 적용가능한 동작이 모두 적용되었다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;f라는 함수가 execute의 함수의 인자로 전달되었고&lt;/li&gt;
&lt;li&gt;함수 내부에서 인자로 받은 함수 func를 문제없이 사용하고 있으며,&lt;/li&gt;
&lt;li&gt;add라는 원 함수의 이름이 마음에 안들었는지 f라는 새이름에 할당했다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;즉, &lt;span style=&quot;background-color: #9feec3;&quot;&gt;파이썬에서 함수는 1급 객체이다. 이 특성이 있어야 Closure가 성립될 수 있다&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;2.3 nonlocal&lt;/h3&gt;
&lt;p&gt;이 개념은 중요하다. 꼭 Closure가 아니라도 의미가 있기에 조금 길게 설명한다.&lt;/p&gt;
&lt;p&gt;먼저 간단한 예시이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1613568838695&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;z = 3

def outer(x):
    y = 10
    def inner():
        x = 1000
        return x

    return inner()

print(outer(10))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;일단 파이썬에서 함수 중첩이 가능하다는 것을 확인했다.&lt;/p&gt;
&lt;p&gt;그건 그런데, 위의 함수에는 x값에 대한 코드가 두 번 제시됐다.&lt;/p&gt;
&lt;p&gt;처음은 함수 실행 시에 받는 임의의 x이고, 다음은 inner함수 내에서 1000으로 초기화하는 변수 x이다.&lt;/p&gt;
&lt;p&gt;예시처럼 함수 호출 시 인자를 10으로 줬을 때 반환되는 값은 몇이어야 하는가??&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;위와 같은 이슈는 결국 scope에 대한 이야기&lt;/span&gt;다. inner함수 입장에서 바라보겠다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;inner함수 블록 안에 있는 영역은 local스코프라고 불린다. 로컬 영역 안의 모든 개체들은 inner의 제어 아래에 있다.&lt;/li&gt;
&lt;li&gt;outer의 안에 있되, inner의 밖에 있는 영역은 nonlocal 스코프라고 불린다. outer의 y변수는 inner입장에서는 nonlocal스코프의 변수이다.&lt;/li&gt;
&lt;li&gt;outer함수 밖의 영역은 global스코프이다. z변수는 global에 선언된 변수로 outer함수 뿐 아니라 다른 코드나 함수에서도 참조 가능할 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이런 global, nonlocal, local의 구분, 다시 말해 스코프의 구분은 어떤 의미가 있는가?&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;각 스코프는 자신의 영역에 최대한의 관심을 가지며, 다른 영역의 변수나 객체에 대해서는 제한적인 제어를 가지게 된다&lt;/span&gt;. &amp;rarr; 객체의 이름이 같다면 local부터 우선순위를 둔다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1613568853791&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def greetings(x):
    def say_hi():
        print(x)

    say_hi()


greetings('안녕하세요?')

안녕하세요?&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;아까의 예와 비슷하다.&lt;/p&gt;
&lt;p&gt;say_hi() 함수는 nonlocal영역의 x의 변수를 그대로 반환하는 기능을 갖고있다.&lt;/p&gt;
&lt;p&gt;그리고 greetings()가 받은 문자열을 문제없이 참조할 수 있었다.&lt;/p&gt;
&lt;p&gt;그러니까&lt;span style=&quot;background-color: #9feec3;&quot;&gt; 외부 스코프의 변수에 대해 '읽기'가 문제없이 가능하다고 이해하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;하지만 자신의 영역이 아닌 영역에 대해 '쓰기'는 제한적이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1613568864542&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def count(x):
    def increment():
        x += 1
        print(x)

    increment()


&amp;gt;&amp;gt;&amp;gt; count(5)

UnboundLocalError: local variable 'x' referenced before assignment&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;음 문제가 뭘까?&lt;/p&gt;
&lt;p&gt;UnboundLocalError로 local변수 x가 할당되기 전에 참조됐다는 의미이다.&lt;/p&gt;
&lt;p&gt;하지만 우리는 nonlocal의 x에 1을 더한건데?&lt;/p&gt;
&lt;p&gt;함정이 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;local영역에서 밖의 영역에 대한 값을 찹조하는, 또는 읽는 것은 항상 문제가 없는데, 값을 수정하거나 새로 할당하는 것은,쓰는 것(write)은 안 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위의 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;예시 코드에서는 'x+=1'가 수정하는 역할&lt;/span&gt;을 하고 있다.&lt;/p&gt;
&lt;p&gt;파이썬에서는 값을 수정하는, 쓰는 코드가 나오면 따로 언급이 없는한, increment함수는 x가 자신이 제어할 수 있는 local변수라고 무조건 가정한다.&lt;/p&gt;
&lt;p&gt;함부로 외부의 변수를 건드리는 어려운 버그를 막기 위해서다.&lt;/p&gt;
&lt;p&gt;선언하지도 않는 변수에 값을 더하는건 당~연히 말이 안되는 거니깐.&lt;/p&gt;
&lt;p&gt;그래서 로컬 스코프에 변수가 할당이 되지 않았다는 에러가 출력된 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;왜 위와 같이 헷갈리게 만들어놨을까?&lt;/p&gt;
&lt;p&gt;그것은 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;코드 영역의 책임과 권한을 명확히 나누는 것이 좋기 때문&lt;/span&gt;이다.&lt;/p&gt;
&lt;p&gt;예제의 count함수는 하나의 내부함수를 갖고 있지만 어떤 경우에는 수십여개의 내부 함수를 가질 수도 있다.&lt;/p&gt;
&lt;p&gt;이때 내부 함수마다 count의 상태값을 건드리고 수정한다면 예상하지 못한 결과를 초래할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그래서 읽기는 가능하지만 쓰기는 제한하고 있는 것이다.&lt;/p&gt;
&lt;p&gt;리눅스에서 모든 파일에 유저마다, 그룹마다 쓰기와 읽기, 실행에 대한 권한을 따로 제어하는 것은 같은 이치다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;만약 위의 예제에서 의도적으로 nonlocal스코프의 값을 수정하고 싶으면 어떻게 할까? 이때 nonlocal statement를 쓰면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1613568902874&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def count(x):
    def increment():
        nonlocal x  # x가 로컬이 아닌 nonlocal의 변수임을 확인한다.
        x += 1
        print(x)

    increment()

count(5)

&amp;gt;&amp;gt;&amp;gt; 6&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;increment 함수 정의 바로 아랫줄에 'nonlocal x'라고 선언했다. 이 코드는 x가 local변수가 아닌, nonlocal, 여기서는 count스코프 내의 변수라는 것을 개발자가 명시적으로 선언하는 것으로 nonlocal영역의 상태에 대해 읽는 것뿐 아니라 쓰는 것도 가능하게 된다.&lt;/p&gt;
&lt;p&gt;같은 식으로 global 스코프의 값을 제어하고 싶다면 'global x'와 같이 사용할 수도 있을 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1613568914176&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;z = 3

def outer(x):
    y = 10
    def inner():
        x = 1000
        return x

    return inner()

&amp;gt;&amp;gt;&amp;gt; print(outer(10))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;자, 멀리 돌아왔다. 우리의 원 질문은, 저 함수를 실행했을 때 값이 outer의 10이 나올지, inner의 1000이 나올지 이다. 결과는~~??&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;명심할점 . 함수는 자신이 제어권을 가장 많이 확보하고 있는, 자신에게 가장 가까운 스코프부터 찾아나아간다.&lt;/p&gt;
&lt;p&gt;inner함수는 자신의 local스코프에서 x를 찾았기 때문에 밖의 스코프를 더 이상 찾아볼 이유가 없다. 그렇기 때문에 outer의 인자로 몇을 주든지 간에 무조건 local x의 값 1000이 나올 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1613568926391&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;outer(1)
outer(10)
outer(100)

&amp;gt;&amp;gt;&amp;gt; 1000
&amp;gt;&amp;gt;&amp;gt; 1000
&amp;gt;&amp;gt;&amp;gt; 1000&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;자, 생각보다 길었지만 Cloure을 이해하기 위한 세 가지 선행개념을 살펴 보았다. 이제 Closure로 넘어가자&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;3. Closure에 대한 이해&lt;/h3&gt;
&lt;p&gt;이제 기본지식을 바탕으로 closure에 대해 알아보자.&lt;/p&gt;
&lt;p&gt;먼저 아리송한 Closure의 단어의 뜻을 잡고 가자.&lt;/p&gt;
&lt;p&gt;기술 용어의 뜻을 제대로 파악하지 못하고 쓰는 것은 절대 지양해야 한다.&lt;/p&gt;
&lt;p&gt;이 단어의 개념을 이해하면 클로저의 정의를 파악하고, 툭징과 사용 시의 장점에 대해 알아본다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;3.1 Closure단어의 의미는?&lt;/h3&gt;
&lt;p&gt;Closure는 여기서 무슨 의미일까? 이 영어 단어의 의미가 파이썬의 클로저에 던지는 메시지는 무엇일까?&lt;/p&gt;
&lt;p&gt;Closure라는 단어에 집찾하기 전에 관련된 단어로 enclose로 우회해서 이해해보자.&lt;/p&gt;
&lt;p&gt;enclose는 close에 대해 en접두사가 붙어 (담,울타리 등으로)'두르다', '둘러치다'란 의미를 갖고 있다. 이 뜻 그대로 이해해보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LR1m2/btqXDf1pfiu/UCinFliHJLtTCdG5JcVAH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LR1m2/btqXDf1pfiu/UCinFliHJLtTCdG5JcVAH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LR1m2/btqXDf1pfiu/UCinFliHJLtTCdG5JcVAH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLR1m2%2FbtqXDf1pfiu%2FUCinFliHJLtTCdG5JcVAH1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;방대한 초원을 공유지로 하여 많은 사람들이 각자의 가축을 기른다고 하자. 누구는 소를 키우고 누구는 양을 키우고... 공유지에서 풀을 먹일 땐 같이 풀어놓더라고, 향후 분명 각자의 소유지에 담을 둘러(enclose) 가축들을 관리할 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;김가네와 박가네가 서로의 우리에 가축동물을 기르고 있다. 이렇게 각자의 영역을 구축하여 동물들을 관리하면 어떤 장점이 있을까?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;당연한 얘기지만, 각자의 동물에 대한 관리와 책임을 명확히 할수 있고, 서로 다른 재산끼리의 불필요한 충돌을 방지할 수 있으면, 각자가 원하는 대로 재산을 관리할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;박가네는 그래프에 대한 깊은 이해가 있어 양들의 배치를 2차원 행렬 그래프에 가깝게 배치했지만, 김가네 목장은 소를 단순히 우리 안에서 방목하고 있다. 각자의 필요나 기호에 맞게 관리한 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;이렇게 어떤 재산이나 속성을 Enclose하여 자신만의 영역을 구축하는 것, 그것의 장점을 이해함으로써 클로저를 더 쉽게 이해할 수 있다.&lt;/b&gt;&lt;/span&gt; 이게 본격적인 정의와 특징을 살펴보면서, 클로저의 장점을 다루며 위의 예시를 상기할 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;3.2 기본 정의&lt;/h3&gt;
&lt;p&gt;이제 진짜 클로저에 대해 살펴보자면.&lt;/p&gt;
&lt;p&gt;파이썬에서 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;클로저는 '자신을 둘러싼 스코프(네임스페이서)의 상태값을 기억하는 함수'&lt;/span&gt;이다.&lt;/p&gt;
&lt;p&gt;그리고 어떤 함수가 클로저이기 위해서는 다음의 세 가지 조건을 만족해야 한다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;해당 함수는 어떤 함수 내의 중첩된 함수여야 한다.&lt;/li&gt;
&lt;li&gt;해당 함수는 자신을 둘러싼(enclose) 함수 내의 상태값을 반드시 참조해야 한다.&lt;/li&gt;
&lt;li&gt;해당 함수를 둘러싼 함수는 이 함수를 반환해야 한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이해를 위해 예시를 들자. 몇 주 전 파이썬에서 팩토리얼을 구하는 5가지 방법에 대한 포스트(&lt;a href=&quot;https://shoark7.github.io/programming/algorithm/several-ways-to-solve-factorial-in-python&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;링크&lt;/a&gt;)를 작성했다. 해당 포스트의 말미에서는 효율을 높이기 위한 Memoization을 제시했는데 이 방법은 5가지 알고리즘에 모두 적용가능하다. 그러면 Memoization 코드를 따로 분리시키고 각 알고리즘에서 그 모듈을 사용한다면 코드를 5번 하드코딩한 것보다 유지보수도 쉽고 가독성도 올라갈 것이 자명하다.(Modularity, that&amp;rsquo;s what matters.)&lt;/p&gt;
&lt;p&gt;해당 포스트에서 캐시 히트 여부를 검사하는 분리된 모듈 코드는 다음과 같았다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1613568993632&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def in_cache(func):
    cache = {}
    def wrapper(n):
        if n in cache:
            return cache[n]
        else:
            cache[n] = func(n)
            return cache[n]
    return wrapper&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;in_cache함수는 cache라는 dict자료구조와 wrapper라는 내장 중첩함수를 가지고 있다.&lt;/p&gt;
&lt;p&gt;이 중첩함수는 정수 n를 받아서 n의 key에 해당하는 value가 cache에 담겨 있으면 반환하고, 아니면 func를 실행해서 값을 저장한 뒤 반환한다.&lt;/p&gt;
&lt;p&gt;이 함수를 실제로 적용해보기 전에, wrapper함수에 대해 살펴보자.&lt;/p&gt;
&lt;p&gt;위에서 어떤 함수의 클로저이기 위한 특징을 살폈는데 공교롭게도 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;이 함수는 클로저이기 위한 조건을 모두 충족&lt;/span&gt;한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;b&gt;in_cache 함수 내의 중첩된 함수이고,&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Enclosing하는 in_cache 스코프의 cache 라는 상태값을 참조하고 있으며,&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;자신을 둘러싼 함수는 자신(wrapper)을 반환하고 있다!&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;일단 wrapper 는 정의상 클로저라는 얘기이고, 이 함수를 제대로 사용해보자.&lt;/p&gt;
&lt;p&gt;위 포스트의 팩토리얼 함수를 하나 가져와서 in_cache 를 적용해보겠다&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1613569021240&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def factorial(n):
    ret = 1
    for i in range(1, n+1):
        ret *= i
    return ret&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;주어진 정수에 대해 팩토리얼을 구하는 초보적인 코드다.&lt;/p&gt;
&lt;p&gt;Memoization과 실제 팩토리얼을 구하는 코드가 분리된 상태로 Memoization을 기능에 추가하려면 다음과 같이 작동시킨다.&lt;/p&gt;
&lt;pre id=&quot;code_1613569030825&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;factorial = in_cache(factorial)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그리고 이해를 위해 wrapper 함수에 코드를 한 줄 추가한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1613569046087&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def in_cache(func):
    cache = {}
    def wrapper(n):
        print(cache) ## !!!!
        if n in cache:
            return cache[n]
        else:
            cache[n] = func(n)
            return cache[n]
    return wrapper


def factorial(n):
    ret = 1
    for i in range(1, n+1):
        ret *= i
    return ret

factorial = in_cache(factorial)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 진짜 핵심으로 들어간다.&lt;/p&gt;
&lt;p&gt;그 클로저라는 wrapper함수 안에 cache의 상태 값을 출력하는 코드를 넣었다.&lt;/p&gt;
&lt;p&gt;저 코드는 wrapper을 enclosing하는 스코프의 cache변수를 추적한다.&lt;/p&gt;
&lt;p&gt;이제 함수를 몇 번 실행해보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1613569061336&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;factorial(3)
factorial(5)
factorial(10)

{}
6

{3: 6}
120


{3: 6, 5: 120}
3628800&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위의 {}결과가 cache의 상태고, 그 아래는 n에 따른 팩토리얼 값이다. 이상한 것을 발견했는가?&lt;/p&gt;
&lt;p&gt;{}가 함수가 실행됨에 따라 업데이트 되고있다.&lt;/p&gt;
&lt;p&gt;이것을 그림으로 표현하면&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d1N4NW/btqXFe8KIrn/OyKaD5dkxSLrpkzHKK5fUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d1N4NW/btqXFe8KIrn/OyKaD5dkxSLrpkzHKK5fUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d1N4NW/btqXFe8KIrn/OyKaD5dkxSLrpkzHKK5fUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd1N4NW%2FbtqXFe8KIrn%2FOyKaD5dkxSLrpkzHKK5fUk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;전역 스코프에는 수많은 변수들이 저장되어 있다. 그중에는 factorial함수도 있다.&lt;/p&gt;
&lt;p&gt;이 함수는 원래 정의한 팩토리얼 함수가 아닌, 'in_cache(dactorial)'의 실행결과 반환된 함수로, 이게 곧 클로저다.(클로저의 조건 3번)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 클로저에 다시 한 번 factorial이라는 이름을 할당해서 사용하는 것뿐, cached_factorial등의 이름을 붙여도 상관은 없다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 클로저는 자체 스코프를 가지고 있어, cache라는 상태는 매번 실행할 때마다 초기화되는 것이 아니라 값이 유지되고있다. 마치 전역 공간에 선언이라도 했듯이 말이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;신기하게도 원래 함수 정의에서 cache 는 wrapper 함수 스코프의 밖에 선언되어 있었다. 그럼에도 wrapper(여기서는 factorial)는 cache 에 접근이 되고, 그 상태를 자신의 스코프 내에서 저장하고 제어할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아까 살펴본 클로저의 정의, **&amp;lsquo;클로저는 자신을 둘러싼 스코프(네임스페이스)의 상태값을 기억하는 함수&amp;rsquo;**는 이런 뜻인 것이다. 이 예에서 factorial 스코프에서는 자신을 둘러싼(enclosing) 스코프의 상태값을(cache) 기억하고 제어할 수 있다.&lt;/p&gt;
&lt;p&gt;이를 통해 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;자신만의 스코프 내에서 함수 실행시마다 초기화되는 것이 아닌, 지속적으로 관리되고 유지되는 cache 를 두고 소기의 목적을 달성할 수 있다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;여기서 궁금한점 wrapper에서 cache[n]=func(n)으로 nonlocal 객체인 cache의 값을 수정했는데 오류가 발생하지 않는 이유는??&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;3.3. Closure의 특징&lt;/h3&gt;
&lt;p&gt;클로저를 사용할 때의 특징과 장점을 살펴보도록 하자.&lt;/p&gt;
&lt;p&gt;일단 눈에 띠는 특징으로 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;클로저는 자신을 둘러싼 함수 스코프의 상태값을 참조하는데, 이 값은 함수가 메모리에서 사라져도 값이 유지가 된다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1613569100624&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def times_multiply(n):
    def multiply(x):
        return n * x
    return multiply


times_3 = times_multiply(3)
times_4 = times_multiply(4)

times_3(5)
times_4(5)


15
20&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제는 다른 예제다. times_multiply 는 n 을 받아서 multiply 라는 함수를 반환한다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;이 multiply 내장 중첩함수는 클로저로 상위 스코프의 n 을 참조하고 있다. 결과 times_3, times_4 는 반환된 클로저로 들어온 숫자에 각각 3, 4를 곱해 반환한다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이때 times_multiply 를 메모리 상에서 삭제하자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1613569112519&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;del(times_multiply)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그러면 &lt;b&gt;이 함수는 더 이상 호출할 수 없는데, 이게 기존에 생성된 클로저에도 영향을 미칠까?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1613569120792&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;times_3(5)


15&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;문제없이 호출가능하다. &lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;클로저는 원 함수에 어떤 변화(심지어는 삭제)가 발생되어도 자신의 스코프는 지킨다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;두 번째 특징으로 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;클로저에서 자신 안에 정의된 내부 변수가 아닌, Enclosing하고 있는 변수에 접근하는 것을 파이썬에서 지원하고 있다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1613569129271&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;times_4.__closure__[0].cell_contents

4&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;b&gt;파이썬 3 기준으로, 클로저 함수는 **closure&lt;/b&gt; 변수를 자동으로 갖고 있다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;이 변수는 튜플 타입으로서 클로저가 enclosing 스코프에서 참조하는 변수들을 담고 있다. 그리고 각 원소의 cell_contents 는 그 값 자체를 갖고 있다&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;위에서는 4라는 값을 감싸는 함수의 인자로 주었기 때문에 저 값은 클로저가 참조하는 값으로 유지된다.&lt;/p&gt;
&lt;p&gt;그리고 이 클로저는 값을 하나만 참조했기 때문에 &lt;b&gt;closure&lt;/b&gt; 는 길이가 1인 튜틀이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1613569168583&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;times_4.__closure__[1].cell_contents


IndexError: tuple index out of range&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;만약 enclosing 스코프에서 값을 여러 개 참조한다면 그 개수만큼 접근가능할 것이다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;클로저가 생성될 때(times_3 = times_multiply(3)) 이 closure&lt;/b&gt; 변수가 같이 생성되고 유지되기 때문에 기존 함수가 삭제되어도 문제없이 클로저를 실행할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그리고 내가 좋아하는 &lt;b&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;decorator는 결국 클로저를 만드는 문법&lt;/span&gt;이다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1613569190160&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;factorial = in_cache(factorial)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;b&gt;in_cache 는 함수를 받아 Memoization 코드를 추가해 다시 함수를 반환하는 함수로, 위에서는 반환된 클로저를 다시 factorial 이라는 이름으로 재할당했다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;이 작업은 데코레이터가 정확히 하는 일이기도 하다.&lt;/p&gt;
&lt;pre id=&quot;code_1613569198823&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@in_cache
def factorial(n):
    ...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;&lt;b&gt;4. Closure 사용시의 장점&lt;/b&gt;&lt;/h2&gt;
&lt;hr /&gt;
&lt;p&gt;그럼 클로저를 사용하면 대체 뭐가 좋을까? 아까의&amp;nbsp;factorial&amp;nbsp;예제를 보자.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;캐시를 자신의 스코프 안에 저장하는 대신 그냥 전역변수로 두고 쓰면 되잖아? 왜 귀찮게 클로저를 만들어서 해야 하는거야?&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;충분히 던질 수 있는 질문이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ICGCw/btqXFfAAVgw/G8R5GFX6sF77aUDkabNU0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ICGCw/btqXFfAAVgw/G8R5GFX6sF77aUDkabNU0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ICGCw/btqXFfAAVgw/G8R5GFX6sF77aUDkabNU0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FICGCw%2FbtqXFfAAVgw%2FG8R5GFX6sF77aUDkabNU0k%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;아까 살펴 본 이미지다. 우리는 공유지에 각 개인의 소유 가축을 모두 풀어놓지 않고 울타리를 둘러쳐 자신의 영역을 구축한다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;결과적으로 이렇게 하는 이유와 Closure 사용의 장점이 일맥상통&lt;/span&gt;한다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;관리와 책임을 명확히 할 수 있고&lt;/li&gt;
&lt;li&gt;각 변수가 섞여 불필요한 충돌을 방지할 수 있으며&lt;/li&gt;
&lt;li&gt;사용환경(context)에 맞게 임의대로 내부구조를 조정할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이 장점을 현실적인 예로 살펴보자. 앞서 우리는&amp;nbsp;factorial&amp;nbsp;함수를 정의했다. 추가로 1부터 N까지의 합을 구해 반환하는&amp;nbsp;sum_to_N&amp;nbsp;함수를 정의해서 같이 사용해야 한다고 하자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1613634684247&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def sum_to_N(N):
    s = 0
    for n in range(N+1):
        s += n
    return s


sum_to_N(10)

55&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;이 함수 또한 빈번하게 호출된다면 Memoization을 통해 효율성을 높일 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그러면, 이런 비슷한 함수가 있을 때마다 전역공간에 cache를 두고 사용할 수 있는가?&lt;/p&gt;
&lt;p&gt;10!은 3628800이고 1부터 10까지의 합은 55다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;전혀 다른 숫자기 때문에 같은 이름의 캐시를 쓸 수 없고, 굳이 전역공간에 캐시를 둔다면 cache_factorial, cache_sum_n과 같은 딱 보기에도 안쓰러운 이름의 변수를 써야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;하지만 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;스코프를 분리해주고 자신의 스코프 안에서만 관리되는 캐시를 쓴다면 두 캐시가 충돌하지 않게 되서 사용성이 훨씬 커진다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;이걸 이렇게 그림으로 표현할 수도 있을까?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dbKji0/btqXKuX6p5I/n4yNUeSlgioXnQmQUviA0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dbKji0/btqXKuX6p5I/n4yNUeSlgioXnQmQUviA0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dbKji0/btqXKuX6p5I/n4yNUeSlgioXnQmQUviA0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdbKji0%2FbtqXKuX6p5I%2Fn4yNUeSlgioXnQmQUviA0k%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;각 함수가 자신만의 스코프 안에서 고유한 cache를 유지하고 이 값을 문제없이 사용한다. 저 캐시들을 전역변수에 할당하고 사용해야 한다면 정말 끔찍할 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;여기서&amp;nbsp;Closure라는 단어의 의미를 이렇게 정의해볼 수도 있겠다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;Closure는 폐쇄라는 뜻을 갖는데, 어떤 굳게 닫힌 문을 연상시킨다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;굳게 닫힌 문이라도 언젠가는 열리기 마련이고, 우리는 이 문을 통해 해당 스코프에 대한 간접적인 접근이 가능하다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;클로저가 하는 일이 바로 그런 것이 아닌가? 클로저를 호출해서 그 스코프 안의 변수에 접근하고 있으니&amp;hellip; 그 이외의 방법으로 접근하는 것은 불가능하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;출처:&lt;/p&gt;
&lt;p&gt;클로저에 대해 아주 잘 설명되어있다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://shoark7.github.io/programming/python/closure-in-python#2b&quot;&gt;https://shoark7.github.io/programming/python/closure-in-python#2b&lt;/a&gt;&lt;/p&gt;</description>
      <category>python</category>
      <category>closure</category>
      <category>PYTHON</category>
      <category>클로져</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/137</guid>
      <comments>https://coding-lks.tistory.com/137#entry137comment</comments>
      <pubDate>Wed, 17 Feb 2021 22:40:18 +0900</pubDate>
    </item>
    <item>
      <title>TDD(Test Driven Development)란 무었인가?</title>
      <link>https://coding-lks.tistory.com/136</link>
      <description>&lt;p id=&quot;SE-d81c88f5-97da-4bcc-9a18-e8fdf485b241&quot;&gt;&lt;span&gt;오늘 날 소프트웨어는 빅데이터, IoT, 인공지능 등의 다양한 분야로 발전하고 있으며, 소프트웨어는 냉장고, 세탁기, TV 등과 같은 백색가전뿐만 아니라 컴퓨터, 핸드폰 등 우리 실생활의 많은 부분에서 사용되고 있다. 소프트웨어는 실생활뿐만 아니라 비행기, 철도, 자동차 등 결함 발생 시 큰 인명피해로 이어질 수 있는 분야에도 광범위하게 사용되고 있다. 따라서 기능뿐만 아니라 안전성까지 보장되는 고품질의 좋은 소프트웨어를 개발해야 한다. 좋은 소프트웨어가 만들어지기 위해서는 잘 동작하고, 결함이 없고, 유지보수가 쉬워야 한다. 그러나 좋은 소프트웨어를 만들기란 고려해야 하는 요소들이 다양하고, 그 종류가 많기 때문에 매우 어려운 작업이다. 하지만 소프트웨어가 나온 지 60여 년이 넘는 현재, 좋은 소프트웨어를 개발하기 위한 소프트웨어 개발 방법들이 많이 존재하며, 본고에서는 그 중에서 테스트와 개발을 같이 진행하여 개발 초기의 오류를 발견하고, 수정하여 좋은 소프트웨어를 개발하기 위한 테스트 주도 개발(TDD, Test Driven Development)방법에 대해 이야기하고자 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;tdd의-정의&quot;&gt;TDD의 정의&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Test Driven Development
&lt;ul&gt;
&lt;li&gt;테스트 주도 개발:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;테스트가 개발을 이끌어 나간다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;구체적인-행동-레벨에서의-tdd의-개념&quot;&gt;구체적인 행동 레벨에서의 TDD의 개념&lt;/h3&gt;
&lt;p&gt;&lt;span&gt;테스트를 먼저 만들고 테스트를 통과하기 위한 것을 짜는 것&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;즉, 만드는 과정에서 우선 테스트를 작성하고 그걸 통과하는 코드를 만들고를 반복하면서 제대로 동작하는지에 대한 피드백을 적극적으로 받는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;보통은 SW 개발을 할 때 코딩을 다 끝나고 난 후 테스트를 한다.
&lt;ul&gt;
&lt;li&gt;코딩이 끝난 후? 개발자가 코딩을 다 짜고 난 후 완성했다고 생각할 때&lt;/li&gt;
&lt;li&gt;이것의 순서를 바꾸는 것이 TDD를 적용하는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;TDD를 적용한 사례
&lt;ul&gt;
&lt;li&gt;예를 들어, 생년월일(input)을 입력받으면 현재 나이(output)를 출력하는 프로그램&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;처음에는 간단한 것으로 목표를 정한다. (태어난 해와 올해의 연도를 입력)
&lt;ul&gt;
&lt;li&gt;2015, 2018 -&amp;gt; (만)3살 우선 이것을 만들겠다는 생각을 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;만들기도 전에 만든 후에 무엇을 테스트할지를 설계한다.
&lt;ul&gt;
&lt;li&gt;2015, 2018를 입력하면 2가 나오는 테스트 프로그램(장차 만들 프로그램을 테스트할 코드)를 만든다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;그 다음에 그 테스트를 통과할 프로그램(1.을 목표로 작성한 코드)를 만든다.
&lt;ul&gt;
&lt;li&gt;올해의 연도 - 태어난 해&lt;/li&gt;
&lt;li&gt;2018 - 2015&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;테스트 프로그램으로 이 프로그램(3.에 해당하는 코드)을 실행한다.&lt;/li&gt;
&lt;li&gt;통과했으면 새로운 테스트를 추가한다.
&lt;ul&gt;
&lt;li&gt;이번에는 생월을 추가했을 때 계산하는 프로그램&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;위와 같은 작업을 계속 왔다갔다 수행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;추상적인-레벨에서의-tdd의-핵심-개념중요&quot;&gt;추상적인 레벨에서의 TDD의 핵심 개념(중요)&lt;/h3&gt;
&lt;p&gt;결정과 피드백 사이의 갭에 대한 인식, 더 나아가&lt;span&gt;&amp;nbsp;&lt;/span&gt;결정과 피드백 사이의 갭을 조절하기 위한 테크닉이라고도 할 수 있다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;켄트 벡(Kent Beck, 익스트림 프로그래밍의 창시자)
&lt;ul&gt;
&lt;li&gt;TDD란?
&lt;ol&gt;
&lt;li&gt;결정과 피드백 사이의 갭에 대한 인식&lt;/li&gt;
&lt;li&gt;결정과 피드백 사이의 갭을 조절하기 위한 테크닉&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;TDD는 프로그래밍 기법이나 기술적인 느낌보다는 심리적인 것으로 볼 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;결정(decision)?
&lt;ul&gt;
&lt;li&gt;프로그램을 하다보면 &amp;lsquo;이 방법으로 해야지&amp;rsquo;, &amp;lsquo;이 부분은 이걸 이용해서 짜야지&amp;rsquo;라는 것을 결정한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;피드백(feedback)?
&lt;ul&gt;
&lt;li&gt;프로그램을 하다보면 성공/실패(에러)라는 피드백을 받는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span&gt;이 둘(결정과 피드백) 사이에 갭이 생긴다.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;갭이 커질수록 문제!&lt;/li&gt;
&lt;li&gt;내가 그 갭을 모르면 더 큰 문제!&lt;/li&gt;
&lt;li&gt;즉, 위의 예와 함께 설명하자면
&lt;ul&gt;
&lt;li&gt;결정: 1.을 목표로 코드를 작성할 때, &amp;lsquo;나는 빼기로 나이를 구해야겠다.&amp;rsquo;라는 것을 결정한다.&lt;/li&gt;
&lt;li&gt;피드백: 빼기로 계산했을 때의 코드를 테스트 프로그램을 실행한 결과로, 된다/안된다라는 프로그램 상의 피드백을 받는다.&lt;/li&gt;
&lt;li&gt;이 둘 사이의 갭을 내가 인식한다면 TDD를 하고 있는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;SE-fa4baa7b-1701-4a87-b8cf-df384266142c&quot;&gt;&lt;span&gt;TDD는 작가가 책을 쓰는 과정과 유사하다. 책을 쓸 때는 목차를 처음 구성한다. 이후 각 목차에 맞는 내용을 구상하여 초안을 작성하고 고쳐쓰기를 반복한다. 이 과정을 TDD에 비유하면 목차구성은 &quot;테스트코드 작성&quot;, 초안작성은 &quot;코드개발&quot;, 고쳐쓰기는 &quot;코드수정&quot;에 해당한다. 반복적인 검토와 고쳐쓰기를 통해서 좋은 글이 완성되는 것처럼 소프트웨어도 반복적인 테스트와 수정을 통해서 고품질의 소프트웨어를 만들 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;SE-74cd36b8-8d02-4383-a19c-64c993344532&quot;&gt;&lt;span&gt;​&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-c97216e5-2007-49d4-8842-e02579b6269e&quot;&gt;&lt;span&gt;&lt;b&gt;TDD 장점&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-153cb48d-2d1e-4109-98cd-1402e4732fa2&quot;&gt;&lt;span&gt;- 작업과 동시에 테스트를 진행함으로써 실시간으로 오류 상황을 파악하여 시스템 결함을 방지&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-1bd0a482-db92-4d51-8ce4-b2b27c72f3f7&quot;&gt;&lt;span&gt;- 짧은 개발 주기를 통해 고객의 요구사항을 빠르게 수용하거나 피드백을 줄 수 있고 현재 진행 상황을 쉽게 파악&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-bbc99320-929c-49d8-b334-c28f5d61bf8c&quot;&gt;&lt;span&gt;- 자동화 도구를 이용해 TDD의 테스트케이스를 단위테스트로 사용이 가능&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-d3babda2-d6ac-40d1-b840-a8f21aaa4411&quot;&gt;&lt;span&gt; 테스트 자동화 도구는 Junit(Java), CppUnit(C/C++), NUnit(.NET) 등이 있음&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-6b58d413-6f90-4d75-a1f0-4fda0f3cbe4c&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;SE-d7a3e0c8-b347-4b77-bdf5-173c70b0e784&quot;&gt;&lt;span&gt;&lt;b&gt;TDD 단점&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-ba2ac2c5-656e-4823-b08a-dc6804b906ad&quot;&gt;&lt;span&gt;- 기존의 개발프로세스에 테스트케이스 설계까지 추가되므로 코드 생산 비용이 높아짐&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-457c3875-ed4b-41af-b5c4-58ed90267b2e&quot;&gt;&lt;span&gt;- 어떻게 테스트를 할 것이며, 프로젝트 성격에 따른 테스트 프레임워크 선택 등 여러 부분에 대한 고려가 필요&lt;/span&gt;&lt;/p&gt;
&lt;p id=&quot;SE-0ab0f007-6308-41c5-8b21-5f08e7b1840d&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;참조:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://m.blog.naver.com/suresofttech/221569611618&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;m.blog.naver.com/suresofttech/221569611618&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://gmlwjd9405.github.io/2018/06/03/agile-tdd.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;gmlwjd9405.github.io/2018/06/03/agile-tdd.html&lt;/a&gt;&lt;/p&gt;</description>
      <category>etc</category>
      <category>agile</category>
      <category>tdd</category>
      <category>소프트웨어 개발 방법론</category>
      <category>애자일</category>
      <category>테스트 주도 개발</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/136</guid>
      <comments>https://coding-lks.tistory.com/136#entry136comment</comments>
      <pubDate>Mon, 15 Feb 2021 22:58:47 +0900</pubDate>
    </item>
    <item>
      <title>[Django] 클래스형 뷰 (CBV, Class-Based View)</title>
      <link>https://coding-lks.tistory.com/135</link>
      <description>&lt;h1&gt;&lt;b&gt;클래스형 뷰 (CBV, Class-Based View)&lt;/b&gt;&lt;/h1&gt;
&lt;p&gt;클래스형 뷰는 상속과 믹스인 기능을 이용하여 코드 재사용하고 뷰를 체계적으로 구성할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;&lt;b&gt;CBV&lt;/b&gt;&lt;b&gt;의 장점&lt;/b&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;GET, POST 등 HTTP 메소드에 따른 처리 코드를 작성할 때 if 함수 대신에 메소드 명으로 코드의 구조가 깔끔하다.&lt;/li&gt;
&lt;li&gt;다중상속 같은 객체지향 기법을 활용해 제너릭 뷰, 믹스인 클래스 등을 사용해 코드의 재사용과 개발 생산성을 높여준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;&lt;b&gt;CBV 사용 가이드라인&lt;/b&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;뷰는 간단 명료해야 한다.&lt;/li&gt;
&lt;li&gt;뷰 코드의 양은 적으면 적을수록 좋다.&lt;/li&gt;
&lt;li&gt;뷰 안에서 같은 코드를 반복적으로 사용하지 않는다.&lt;/li&gt;
&lt;li&gt;뷰는 프레젠테이션 로직에서 관리하고 비즈니스 로직은 모델에서 처리한다. 매우 특별한 경우에만 폼에서 처리한다.&lt;/li&gt;
&lt;li&gt;403, 404, 500 에러 핸들링에는 CBV를 이용하지 않고 FBV를 이용한다.&lt;/li&gt;
&lt;li&gt;믹스인은 간단명료해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;제너릭 뷰와 상속&lt;/b&gt;&lt;/h1&gt;
&lt;p&gt;Generic View (제네릭뷰) 제네릭뷰란 장고에서 기본적으로 제공하고 있는 뷰의 형태로, 개발할 때 자주 등장하는 내용을 모아놓은 뷰를 뜻한다&lt;/p&gt;
&lt;p&gt;제너릭 뷰의 4가지 분류&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;기반 뷰(Base View): 뷰 클래스를 생성하고 다른, 제너릭 뷰의 부모 클래스가 되는 기본 제너릭 뷰&lt;/li&gt;
&lt;li&gt;제너릭 보기 뷰(Generic Display View): 객체의 목록 또는 하나의 객체 상세 정보를 보여주는 뷰&lt;/li&gt;
&lt;li&gt;제너릭 수정 뷰(Generic Edit View): 폼을 통해 객체를 생성, 수정, 삭제하는 기능을 제공하는 뷰&lt;/li&gt;
&lt;li&gt;제너릭 날짜 뷰(Generic Date View): 날짜 기반 객체의 연/월/일 페이지로 구분해 보여주는 뷰&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;&lt;b&gt;주요 제너릭 뷰 목록&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;기반 뷰(Base View)&lt;/b&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;View: 최상위 부모 제너릭 뷰 클래스&lt;/li&gt;
&lt;li&gt;TemplateView: 주어진 템플릿으로 렌더링&lt;/li&gt;
&lt;li&gt;RedirectView: 주어진 URL로 리다이렉트&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;제너릭 보기 뷰(Generic Display View)&lt;/b&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;DetailView: 조건에 맞는 하나의 객체 출력&lt;/li&gt;
&lt;li&gt;ListView: 조건에 맞는 객체 목록 출력&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;제너릭 수정 뷰(Generic Edit View)&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;FormView: 폼이 주어지면 해당 폼을 출력&lt;/li&gt;
&lt;li&gt;CreateView: 객체를 생성하는 폼 출력&lt;/li&gt;
&lt;li&gt;UpdateView: 기존 객체를 수정하는 폼을 출력&lt;/li&gt;
&lt;li&gt;DeleteView: 기존 객체를 삭제하는 폼을 출력&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;제너릭 날짜 뷰(Generic Date View)&lt;/b&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;YearArchiveView: 주어진 연도에 해당하는 객체 출력&lt;/li&gt;
&lt;li&gt;MonthArchiveView: 주어진 월에 해당하는 객체 출력&lt;/li&gt;
&lt;li&gt;DayArchiveView: 주어진 날짜에 해당하는 객체 출력&lt;/li&gt;
&lt;li&gt;TodayArchiveView: 오늘 날짜에 해당하는 객체 출력&lt;/li&gt;
&lt;li&gt;DateDetailView: 주어진 연, 월, 일 PK(또는 슬러그)에 해당하는 객체 출력&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;&lt;b&gt;제너릭 뷰 오버라이딩&lt;/b&gt;&lt;/h2&gt;
&lt;h3&gt;&lt;b&gt;속성 변수 오버라이딩&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;model&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;기본 뷰(View, Template, RedirectView) 3개를 제외하고 모든 제너릭 뷰에서 사용한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;queryset&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;기본 뷰(View, Template, RedirectView) 3개를 제외하고 모든 제너릭 뷰에서 사용한다.&amp;nbsp;queryset을 사용하면&amp;nbsp;model&amp;nbsp;속성은 무시된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;template_name&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;TemplateView를 포함한 모든 제너릭 뷰에서 사용한다. 템플릿 파일명을 문자열로 지정한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;context_object_name&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;뷰에서 템플릿 파일에 전달하는 컨텍스트 변수명을 지정한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;paginate_by&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;ListView와 날짜 기반 뷰(예, YearArchiveView)에서 사용한다. 페이징 기능이 활성화 된 경우 페이지당 출력 항목 수를 정수로 지정한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;date_field&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;날짜 기반 뷰(예, YearArchiveView)에서 사용한다. 이 필드의 타입은 DateField 또는 DateTimeField이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;form_class&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;FormView, CreateView, UpdateView에서 폼을 만드는데 사용할 클래스를 지정한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;success_url&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;FormView, CreateView, UpdateView, DeleteView에서 폼에 대한 처리가 성공한 후 리디이렉트할 URL 주소이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;메소드 오버라이딩&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;def get_queryset()&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;기본 뷰(View, Template, RedirectView) 3개를 제외하고 모든 제너릭 뷰에서 사용한다. 디폴트는&amp;nbsp;queryset&amp;nbsp;속성을 반환한다.&amp;nbsp;queryset&amp;nbsp;속성이 지정되지 않은 경우 모델 매니저 클래스의 all() 메소드를 호출해 QuerySet 객체를 생성해 반환한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;def get_context_data(**kwargs)&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;뷰에서 템플릿 파일에 넘겨주는 컨텍스트 데이터를 추가하거나 변경하는 목적으로 오버라이딩한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;&lt;b&gt;def form_valid(form)&lt;/b&gt;&lt;/h3&gt;
&lt;h3&gt;&lt;b&gt;모델을 지정하는 방법 3가지&lt;/b&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;model 속성 변수 지정&lt;/li&gt;
&lt;li&gt;queryset 속성 변수 지정&lt;/li&gt;
&lt;li&gt;def get_queryset()&amp;nbsp;메소드 오버라이딩&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;예제 코드&lt;/b&gt;&lt;/h1&gt;
&lt;pre id=&quot;code_1613309363382&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from django.views.generic import ListView, DetailView
from .models import Question


class IndexView(ListView):
    template_name = 'cbvpolls/index.html'
    context_object_name = 'latest_question_list'

    def get_queryset(self):
        return Question.objects.order_by('-pub_date')[:5]


class DetailView(DetailView):
    model = Question
    template_name = 'cbvpolls/detail.html'


class ResultsView(DetailView):
    model = Question
    template_name = 'cbvpolls/results.html'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;믹스인 다중상속&lt;/b&gt;&lt;/h1&gt;
&lt;p&gt;장고에선 다중 상속과 모양이 같습니다. 하지만 차이점은 상속되는 클래스가 Mixin의 경우엔 재사용되는 code의 fragment라는 점, 즉 스스로는 별 의미를 갖고 있지 않다는 점입니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;함수형 뷰 (FBV, Function-Based View)&lt;/b&gt;&lt;/h1&gt;
&lt;h1&gt;&lt;b&gt;예제 코드&lt;/b&gt;&lt;/h1&gt;
&lt;pre id=&quot;code_1613309352695&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from django.shortcuts import render, get_object_or_404
from django.core.urlresolvers import reverse
from .models import Question


def index(request):
    latest_question_list = Question.objects.all().order_by('-pub_date')[:5]
    context = {'latest_question_list': latest_question_list}
    return render(request, 'polls/index.html', context)


def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/detail.html', {'question': question})


def results(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/results.html', {'question': question})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;출처:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://wikidocs.net/9623&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;wikidocs.net/9623&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://bakjuna.tistory.com/81&quot;&gt;https://bakjuna.tistory.com/81&lt;/a&gt;&lt;/p&gt;</description>
      <category>django</category>
      <author>coding of lks</author>
      <guid isPermaLink="true">https://coding-lks.tistory.com/135</guid>
      <comments>https://coding-lks.tistory.com/135#entry135comment</comments>
      <pubDate>Sun, 14 Feb 2021 22:31:33 +0900</pubDate>
    </item>
  </channel>
</rss>