<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>yoon-dh.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Thu, 13 Jul 2023 08:08:32 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>yoon-dh.log</title>
            <url>https://images.velog.io/images/yoon-dh/profile/0415a392-54d1-4d4f-8d2b-a311214a253e/social.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. yoon-dh.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/yoon-dh" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[redux-toolkit : createSlice, createAsyncThunk]]></title>
            <link>https://velog.io/@yoon-dh/redux-toolkit-createSlice-createAsyncThunk</link>
            <guid>https://velog.io/@yoon-dh/redux-toolkit-createSlice-createAsyncThunk</guid>
            <pubDate>Thu, 13 Jul 2023 08:08:32 GMT</pubDate>
            <description><![CDATA[<h2 id="redux-toolkit">[Redux toolkit]</h2>
<ul>
<li>설정</li>
<li>미들웨어 설치 </li>
<li>반복되는 코드 </li>
<li>불변성 유지
==&gt; 이런 불편한 것들을 해소</li>
</ul>
<pre><code>$ npm install @reduxjs/toolkit</code></pre><h3 id="createslice">createSlice</h3>
<ul>
<li><p>reducer 함수의 객체, 슬라이스 이름, 초기 상태 값을 받아들여서 해당 액션 생성자와 액션 유형으로 slice reducer를 자동으로 생성함</p>
</li>
<li><p>createSlice()는 다음과 같은 파라미터로 구성되어 있다. </p>
<ul>
<li>name : 해당 모듈의 이름을 작성</li>
<li>initialState : 해당 모듈의 초기값을 세팅한다.</li>
<li>reducer : 리듀서를 작성한다. 이때 해당 리듀서의 키값으로 액션 함수가 자동으로 생성</li>
</ul>
</li>
</ul>
<pre><code class="language-javascript">export const postsSlice = createSlice({
  name: &#39;posts&#39;, // 이름
  initialState, // 초기값
  reducers: { // posts.actions
    setPosts: (state, { payload }) =&gt; { // action.payload
      state.selPosts = payload;
    },
  },
  // extraReducers는 createSlice가 생성한 액션 타입 외 다른 액션 타입에 응답할 수 있도록 합니다.
  // 아래처럼 슬라이스 외부에서 액션 타입을 참조하여 상태를 변화시킬 수 있습니다.
  extraReducers: (builder) =&gt; {
    builder
      .addCase(getAllPosts.fulfilled, (state, { payload }) =&gt; {
        state.allPosts = payload; // fulfilled 성공
      })
      .addCase(getAllPosts.rejected, (state, { payload }) =&gt; {
        console.log(&#39;rejected&#39;, payload); // rejected 실패
      });
  },
});

export const { setPosts } = postsSlice.actions;
// actions 는 내보내줘야 함(외부 컴포넌트에서 접근 가능하도록)
export default postsSlice.reducer;</code></pre>
<ul>
<li>createSlice에서는 state값을 mutate하는 것이 가능 </li>
</ul>
<ol>
<li>원본에 추가,삭제,수정 등 mutate 하는 방식</li>
<li>이전과 같은 방식으로 새로운 배열 혹은 객체로 대체하는 방식 </li>
</ol>
<h3 id="createasyncthunk">createAsyncThunk</h3>
<ul>
<li><p>액션 타입 문자열, 프로미스를 반환하는 비동기 함수, 추가 옵션 순서대로 인자를 받는 함수다.
입력받은 액션 타입 문자열을 기반으로 프로미스 라이프사이클 액션 타입을 생성하고, thunk action creator를 반환한다.</p>
</li>
<li><p>thunk action creator: 프로미스 콜백을 실행하고 프로미스를 기반으로 라이프사이클 액션을 디스패치한다.</p>
</li>
<li><p>리듀서를 생성해주는 기능은 없기 때문에 액션들을 처리할 로직을 직접 작성해야 한다.</p>
</li>
</ul>
<pre><code class="language-javascript">import { createAsyncThunk, createSlice } from &#39;@reduxjs/toolkit&#39;
import { userAPI } from &#39;./userAPI&#39;

const fetchUserById = createAsyncThunk(
  // string action type value: 이 값에 따라 pending, fulfilled, rejected가 붙은 액션 타입이 생성된다.
  &#39;users/fetchByIdStatus&#39;,
  // payloadCreator callback: 비동기 로직의 결과를 포함하고 있는 프로미스를 반환하는 비동기 함수
  async (userId, thunkAPI) =&gt; {
    const response = await userAPI.fetchById(userId);
    return response.data;
  },
  // 세 번째 파라미터로 추가 옵션을 설정할 수 있다.
  // condition(arg, { getState, extra } ): boolean (비동기 로직 실행 전에 취소하거나, 실행 도중에 취소할 수 있다.)
  // dispatchConditionRejection: boolean (true면, condition()이 false를 반환할 때 액션 자체를 디스패치하지 않도록 한다.)
  // idGenerator(): string (requestId를 만들어준다. 같은 requestId일 경우 요청하지 않는 등의 기능을 사용할 수 있게 된다.)
);</code></pre>
<p>createAsyncThunk는 thunk action creator를 반환한다.
위의 경우를 예로 들면, 다음 세 가지 thunk action creator가 반환된다.
fetchUserById.pending: &#39;users/fetchByIdStatus/pending&#39; 액션을 디스패치하는 thunk action creator
fetchUserById.fulfilled: &#39;users/fetchByIdStatus/fulfilled&#39; 액션을 디스패치하는 thunk action creator
fetchUserById.rejected: &#39;users/fetchByIdStatus/rejected&#39; 액션을 디스패치하는 thunk action creator
이 액션들이 디스패치되면, thunk는 아래 과정을 실행한다.
pending 액션을 디스패치한다.
payloadCreator 콜백을 호출하고 프로미스가 반환되기를 기다린다.
프로미스가 반환되면, 프로미스의 상태에 따라 다음 행동을 실행한다.
프로미스가 이행된 상태라면, action.payload를 fulfilled 액션에 담아 디스패치한다.
프로미스가 거부된 상태라면, rejected 액션을 디스패치하되 rejectedValue(value) 함수의 반환값에 따라 액션에 어떤 값이 넘어올지 결정된다.
rejectedValue가 값을 반환하면, action.payload를 reject 액션에 담는다.
rejectedValue가 없거나 값을 반환하지 않았다면, action.error 값처럼 오류의 직렬화된 버전을 reject 액션에 담는다.
디스패치된 액션이 어떤 액션인지에 상관없이, 항상 최종적으로 디스패치된 액션을 담고 있는 이행된 프로미스를 반환한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Vite + React + eslint setting]]></title>
            <link>https://velog.io/@yoon-dh/Vite-React-eslint-setting</link>
            <guid>https://velog.io/@yoon-dh/Vite-React-eslint-setting</guid>
            <pubDate>Wed, 12 Jul 2023 05:15:08 GMT</pubDate>
            <description><![CDATA[<pre><code>$ npm install -g pnpm</code></pre><pre><code>pnpm add -D vite</code></pre><ul>
<li>package.json
<img src="https://velog.velcdn.com/images/yoon-dh/post/38f7ba0c-0828-4f6c-9af6-21aa1b7b1f99/image.png" alt=""></li>
</ul>
<hr>
<pre><code>$ pnpm create vite@latest</code></pre><p>@ 뒤에 버전 입력! latest ==&gt; 최신버전</p>
<pre><code>$ pnpm i </code></pre><h4 id="eslint-setting">ESlint setting</h4>
<pre><code>$ pnpm create @eslint/config</code></pre><p>To check syntax and find problems
선택</p>
<p>JavaScript modules (import/export) 선택 </p>
<p>React 선택 </p>
<p>TypeScript = no </p>
<p>code run? a 눌러서 browser,node 둘다 선택 </p>
<p>config file format ==&gt; JavaScript 선택</p>
<p>eslint-plugin-react ==&gt; Yes</p>
<p>package manager ==&gt; pnpm</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/4756ebbd-b958-4c97-9458-395b82ced61e/image.png" alt=""></p>
<p>VScode ESlint install 필수</p>
<pre><code>$ pnpm add -D eslint-plugin-jsx-a11y</code></pre><p><a href="https://www.npmjs.com/package/eslint-plugin-jsx-a11y">https://www.npmjs.com/package/eslint-plugin-jsx-a11y</a></p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/9afdd874-c499-4e33-a8e8-99a284f6a02a/image.png" alt=""></p>
<p>.eslintrc.cjs 파일 내부 
plugin &amp; extends에 위와 같이 추가</p>
<pre><code>$ pnpm add -D eslint-plugin-react-hooks</code></pre><p>이건 빌드하면 install 되어는 있음 
아래 변경 사항만 추가해주면 될 듯</p>
<p><a href="https://www.npmjs.com/package/eslint-plugin-react-hooks">https://www.npmjs.com/package/eslint-plugin-react-hooks</a></p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/7a4c7e96-5bb3-4411-9005-e9f55027bbf6/image.png" alt=""></p>
<pre><code>$ pnpm add -D eslint-plugin-react-refresh eslint-plugin-import</code></pre><p>react refresh는 install 되어있음 
plugin-import는 없음 install 필요</p>
<p><a href="https://www.npmjs.com/package/react-refresh">https://www.npmjs.com/package/react-refresh</a></p>
<ul>
<li>파일 내 추가 사항은 final check 파일들 참조</li>
</ul>
<pre><code>$ pnpm add -D @types/node</code></pre><h2 id="final-check">final check</h2>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/9b106daa-abb9-4d58-b0c2-8e91e8787401/image.png" alt=""></p>
<ul>
<li>pnpm list</li>
</ul>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/422bac34-33a1-468b-b23a-8f8aa6e8514e/image.png" alt=""></p>
<ul>
<li>.eslintrc.cjs</li>
</ul>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/fa98c651-63c5-435a-937e-e27288acb632/image.png" alt=""></p>
<ul>
<li>package.json</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[React 개발환경 & 기본]]></title>
            <link>https://velog.io/@yoon-dh/React-%EA%B0%9C%EB%B0%9C%ED%99%98%EA%B2%BD-%EA%B8%B0%EB%B3%B8</link>
            <guid>https://velog.io/@yoon-dh/React-%EA%B0%9C%EB%B0%9C%ED%99%98%EA%B2%BD-%EA%B8%B0%EB%B3%B8</guid>
            <pubDate>Thu, 29 Jun 2023 07:22:03 GMT</pubDate>
            <description><![CDATA[<h2 id="개발-환경">[개발 환경]</h2>
<pre><code>$ npx create-react-app</code></pre><p>node.js 가 설치되어 있어야 함!</p>
<pre><code>$ npm start</code></pre><p>react sample application </p>
<pre><code>$ npm serve -s build</code></pre><p>배포 </p>
<hr>
<h2 id="react란">[React란?]</h2>
<p>React ==&gt; 사용자 정의 태그(<strong>컴포넌트</strong>)를 만드는 것</p>
<p>function을 이용해 사용자 정의 태그 만듬
반드시 <strong>대문자로 시작</strong>할 것 !</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/27f0438e-43b2-467f-a48c-b46e903bdff6/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[test]]></title>
            <link>https://velog.io/@yoon-dh/test</link>
            <guid>https://velog.io/@yoon-dh/test</guid>
            <pubDate>Tue, 23 May 2023 14:41:42 GMT</pubDate>
            <description><![CDATA[<p>➡️</p>
<p>↪️</p>
<p>↳</p>
<p>↪</p>
<p><em>written by yoon</em></p>
<p>★★★★★</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[DRF Auth System]]></title>
            <link>https://velog.io/@yoon-dh/DRF-Auth-System</link>
            <guid>https://velog.io/@yoon-dh/DRF-Auth-System</guid>
            <pubDate>Mon, 15 May 2023 08:31:20 GMT</pubDate>
            <description><![CDATA[<h1 id="authentication--authorization">[Authentication &amp; Authorization]</h1>
<h2 id="authentication---인증-입증">Authentication - 인증, 입증</h2>
<ul>
<li>자신이라고 주장하는 사용자가 누구인지 확인하는 행위 </li>
<li>모든 보안 프로세스의 첫 번째 단계(가장 기본 요소)</li>
<li>즉, 내가 누구인지를 확인하는 과정</li>
<li>401 Unauthorized<ul>
<li>비록 HTTP 표준에서는 &quot;미승인(unauthorized)&quot;을 명확히 하고 있지만, 의미상 이 응답은 &quot;비인증(unauthenticated)&quot;을 의미</li>
</ul>
</li>
</ul>
<h2 id="authorization---권한-부여-허가">Authorization - 권한 부여, 허가</h2>
<ul>
<li>사용자에게 특정 리소스 또는 기능에 대한 액세스 권한을 부여하는 과정(절차)</li>
<li>보안 환경에서 권한 부여는 항상 인증이 먼저 필요함<ul>
<li>사용자는 조직에 대한 액세스 권한을 부여받기 전에 먼저 자신의 ID가 진짜인지 먼저 확인해야 함 </li>
</ul>
</li>
<li>서류의 등급, 웹페이지에서 글을 조회 &amp; 삭제 &amp; 수정 할 수 있는 방법, 제한 구역<ul>
<li>인증이 되었어도 모든 권한을 부여 받는 것은 아님 </li>
</ul>
</li>
<li>403 Forbidden<ul>
<li>401과 다른 점은 서버는 클라이언트가 누구인지 알고 있음 </li>
</ul>
</li>
</ul>
<h2 id="authentication-and-authorization-work-together">Authentication and Authorization work together</h2>
<ul>
<li>회원가입 후, 로그인 시 서비스를 이용할 수 있는 권한 생성<ul>
<li>인증 이후에 권한이 따라오는 경우가 많음 </li>
</ul>
</li>
<li>단 모든 인증을 거쳐도 권한이 동일하게 부여되는 것은 아님<ul>
<li>Django에서 로그인을 했더라도 다른 사람의 글까지 수정/삭제가 가능하진 않음</li>
</ul>
</li>
<li>세션, 토큰, 제 3자를 활용하는 등의 다양한 인증 방식이 존재</li>
</ul>
<h1 id="authentication-determined">[Authentication determined]</h1>
<h2 id="다양한-인증-방식">다양한 인증 방식</h2>
<ul>
<li><p>BasicAuthentication</p>
<ul>
<li>가장 기본적인 수준의 인증 방식</li>
<li>테스트에 적합</li>
</ul>
</li>
<li><p>SessionAuthentication</p>
<ul>
<li>Django에서 사용하였던 session 기반의 인증 시스템</li>
<li>DRF와 Django의 session 인증 방식은 보안적 측면을 구성하는 방법에 차이가 있음 </li>
</ul>
</li>
<li><p>RemoteUserAuthentication</p>
<ul>
<li>Django의 Remote user 방식을 사용할 때 활용하는 인증 방식 </li>
</ul>
</li>
<li><p>TokenAuthentication</p>
<ul>
<li>매우 간단하게 구현할 수 있음 </li>
<li>기본적인 보안 기능 제공 </li>
<li>다양한 외부 패키지가 있음</li>
</ul>
</li>
</ul>
<p>[중요!] settings.py에서 <strong>DEFAULT_AUTHENTICATION_CLASSES</strong>를 정의</p>
<ul>
<li>TokenAuthentication 인증 방식을 사용할 것임을 명시</li>
</ul>
<h2 id="tokenauthentication-사용-방법">[TokenAuthentication 사용 방법]</h2>
<ul>
<li>settings.py &gt; INSTALLED_APPS에 rest_framework.authtoken 등록</li>
</ul>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/614c1201-bcea-4964-a05d-d62fe1a804c1/image.png" alt=""></p>
<ul>
<li>각 User 마다 고유 Token 생성</li>
</ul>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/99c9c963-2363-4f3d-b4b7-522134a9ca8f/image.png" alt=""></p>
<ul>
<li>생성한 Token을 각 User에게 발급<ul>
<li>User는 발급 받은 Token을 요청과 함께 전송</li>
<li>Token을 통해 User 인증 및 권한 확인 </li>
</ul>
</li>
<li>Token 발급 방법</li>
</ul>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/40bce80b-2de7-4d8e-9eec-8933f38c1abd/image.png" alt=""></p>
<ul>
<li>User는 발급 받은 Token을 headers에 담아 요청과 함께 전송 <ul>
<li>단, 반드시 Token 문자열 함께 삽입</li>
<li>삽입해야할 문자열은 각 인증 방식마다 다름(ex. Bearer, Auth, JWT 등)</li>
<li>Token 문자열과 발급받은 실제 token 사이를 &#39; &#39;(공백)으로 구분 </li>
</ul>
</li>
<li>Authorization HTTP headers 작성 방법</li>
</ul>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/1ef629d7-0536-408f-8cbb-50619fd344c4/image.png" alt=""></p>
<h3 id="토큰-생성-및-관리-문제점">토큰 생성 및 관리 문제점</h3>
<ul>
<li>기본 제공 방식에서 고려하여야 할 사항들 </li>
</ul>
<ol>
<li>Token 생성 시점 </li>
<li>생성한 Token 관리 방법</li>
<li>User와 관련된 각종 기능 관리 방법<ul>
<li>회원가입, 로그인, 회원 정보 수정, 비밀번호 변경 등..</li>
</ul>
</li>
</ol>
<h2 id="dj-rest-auth">[dj-rest-auth]</h2>
<ul>
<li>회원가입, 인증(소셜미디어 인증 포함), 비밀번호 재설정, 사용자 세부 정보 검색, 회원 정보 수정 등을 위한 REST API end point 제공</li>
<li>django-rest-auth는 더 이상 업데이트를 지원하지 않음 ==&gt; dj-rest-auth 사용 </li>
</ul>
<p>**[dj-rest-auth] 사용 방법 **</p>
<ol>
<li>패키지 설치 <pre><code>$ pip install &#39;dj-rest-auth[with_social]&#39;</code></pre></li>
</ol>
<p>2.App 등록</p>
<ul>
<li>settings.py &gt; INSTALLED_APPS에 &#39;dj-rest-auth&#39; 등록</li>
</ul>
<ol start="3">
<li>url 등록 </li>
</ol>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/8fc57133-e266-4730-b8e3-ca6145576480/image.png" alt=""></p>
<p>[before start]</p>
<ul>
<li>시작하기 전 auth.User를 accounts.User로 변경 필요<ul>
<li>settings.py &gt; AUTH_USER_MODEL = &#39;accounts.User&#39;</li>
</ul>
</li>
<li>auth.User로 설정된 DB제거</li>
<li>dq.sqlite3 삭제 </li>
<li>migrations의 파일들 삭제</li>
</ul>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/16ba4afb-99e9-4206-b70f-708a4d1fd9d6/image.png" alt=""></p>
<ul>
<li>my_api/settings.py에 추가</li>
<li>App 등록 및 SITE_ID 설정</li>
</ul>
<p>[SITE_ID]</p>
<ul>
<li>Django는 하나의 컨텐츠를 기반으로 여러 도메인에 컨텐츠를 게시 가능하도록 설계됨</li>
<li>다수의 도메인이 하나의 데이터베이스에 등록</li>
<li>현재 프로젝트가 첫번째 사이트임을 나타냄</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Vue Router]]></title>
            <link>https://velog.io/@yoon-dh/Vue-Router</link>
            <guid>https://velog.io/@yoon-dh/Vue-Router</guid>
            <pubDate>Tue, 09 May 2023 03:16:00 GMT</pubDate>
            <description><![CDATA[<h1 id="vue-router">Vue Router</h1>
<ul>
<li>Vue의 공식 라우터</li>
<li>SPA 상에서 라우팅을 쉽게 개발할 수 있는 기능을 제공</li>
<li>라우트(routes)에 컴포넌트를 매핑한 후, 어떤 URL에서 렌더링 할지 알려줌<ul>
<li>즉, SPA를 MPA처럼 URL을 이동하면서 사용 가능</li>
<li>SPA의 단점 중 하나인 &quot;URL이 변경되지 않는다&quot;를 해결</li>
</ul>
</li>
</ul>
<pre><code>$ vue create vue-router-app

$ cd vue-router-app

$ vue add router </code></pre><ul>
<li>기존에 프로젝트를 진행하고 있던 도중에 router를 추가하게 되면 App.vue를 덮어쓰므로 필요한 경우 명령을 실행하기 전에 파일을 백업해두어야 함</li>
</ul>
<p><strong>[History mode]</strong></p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/a2856abc-eeca-4d18-b2a9-45a20f54930b/image.png" alt=""></p>
<ul>
<li>브라우저의 History API를 활용한 방식<ul>
<li>새로고침 없이 URL 이동 기록을 남길 수 있음</li>
</ul>
</li>
<li>우리에게 익숙한 URL 구조로 사용 가능<ul>
<li>ex) <a href="http://localhost:8000/index">http://localhost:8000/index</a></li>
</ul>
</li>
<li>History mode를 사용하지 않으면 Default 값인 hash mode로 설정(&#39;#&#39;을 통해 URL을 구분하는 방식)<ul>
<li>ex) <a href="http://localhost:8080#index">http://localhost:8080#index</a></li>
</ul>
</li>
</ul>
<p><strong>[App.vue]</strong></p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/0cf368a6-36d1-4564-9016-1ed4085f607b/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/1dd455d7-ee20-4a59-ae61-6cc359cc5f67/image.png" alt=""></p>
<ul>
<li>router/index.js</li>
<li>views 폴더 생성</li>
</ul>
<h2 id="router-link">[router-link]</h2>
<ul>
<li>a태그와 비슷한 기능 ==&gt; URL을 이동시킴<ul>
<li>routes에 등록된 컴포넌트와 매핑</li>
<li>히스토리 모드에서 router-link는 클릭 이벤트로 차단하여 a태그와 달리 브라우저가 페이지를 다시 로드 하지 않도록 함</li>
</ul>
</li>
<li>목표 경로는 &#39;to&#39; 속성으로 지정됨</li>
<li>기능에 맞게 HTML에서 a태그로 rendering 되지만, 필요에 따라 다른 태그로 바꿀 수 있음</li>
</ul>
<h2 id="router-view">[router-view]</h2>
<ul>
<li>주어진 URL에 대해 일치하는 컴포넌트를 렌더링 하는 컴포넌트</li>
<li>실제 component가 DOM에 부착되어 보이는 자리를 의미</li>
<li>router-link를 클릭하면 routes에 매핑된 컴포넌트를 렌더링</li>
<li>django에서의 block tag와 비슷함<ul>
<li>App.vue == base.html</li>
<li>router-view == block 태그로 감싼 부분</li>
</ul>
</li>
</ul>
<h2 id="srcrouterindexjs">[src/router/index.js]</h2>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/973f0a8c-13e2-402a-a843-5ca96b211afd/image.png" alt=""></p>
<ul>
<li>라우터에 관련된 정보 및 설정이 작성되는 곳</li>
<li>Django에서의 urls.py에 해당</li>
<li>routes에 URL과 컴포넌트를 매핑</li>
</ul>
<h2 id="srcviews">[src/Views]</h2>
<ul>
<li>router-view에 들어갈 component 작성</li>
<li>기존에 컴포넌트를 작성하던 곳은 components 폴더 뿐이었지만 이제 두 폴더로 나뉘어짐</li>
<li>각 폴더 안의 .vue 파일들이 기능적으로 다른 것은 아님 </li>
<li>폴더별 컴포넌트 배치는 다음과 같이 진행</li>
<li><em><strong>views/</strong></em><ul>
<li>routes에 매핑되는 컴포넌트, 즉 router-view 태그의 위치에 렌더링되는 컴포넌트를 모아두는 폴더</li>
<li>다른 컴포넌트와 구분하기 위해 View로 끝나도록 파일이름 만드는 것 권장</li>
</ul>
</li>
<li><em><strong>components/</strong></em><ul>
<li>routes에 매핑된 컴포넌트의 하위 컴포넌트를 모아두는 폴더</li>
</ul>
</li>
</ul>
<h1 id="vue-router-주소-이동">Vue Router 주소 이동</h1>
<ol>
<li>선언적 방식 네비게이션</li>
<li>프로그래밍 방식 네비게이션</li>
</ol>
<h2 id="1선언적-방식-네비게이션">[1.선언적 방식 네비게이션]</h2>
<p><em>파일 경로 ==&gt; App.vue</em>
<img src="https://velog.velcdn.com/images/yoon-dh/post/8ac81f06-5b47-4edb-9182-0fd1d2110c4b/image.png" alt=""></p>
<ul>
<li>router-link의 &#39;to&#39; 속성으로 주소 전달<ul>
<li>routes에 등록된 주소와 매핑된 컴포넌트로 이동 </li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/8aec17f8-633b-4409-ba6f-9d23fde2e1ce/image.png" alt=""></p>
<ul>
<li>Named Routes<ul>
<li>이름을 가지는 routes</li>
<li>Django에서 path 함수의 name 인자의 활용과 같은 방식 </li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/ee7db95a-b254-468a-b561-a8b36574f2b2/image.png" alt=""></p>
<ul>
<li>동적인 값을 사용하기때문에 v-bind를 사용해야 정상적으로 작동</li>
</ul>
<h2 id="2프로그래밍-방식-네비게이션">[2.프로그래밍 방식 네비게이션]</h2>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/8110fb58-c028-4632-99b1-b8d6df6aca20/image.png" alt=""></p>
<ul>
<li>Vue 인스턴스 내부에서 라우터 인스턴스에 $router 로 접근할 수 있음</li>
<li>다른 URL로 이동하려면 this.$router.push 를 사용 <ul>
<li>history stack에 이동할 URL을 넣는 방식</li>
<li>history stack에 기록이 남기 때문에 사용자가 브라우저의 뒤로가기 버튼을 클릭하면 이전 URL로 이동할 수 있음 </li>
</ul>
</li>
<li>결국 <strong>router-link :to=&quot;...&quot;</strong> 를 클릭하는 것과 <strong>$router.push(...)</strong>를 호출하는 것은 같은 동작</li>
</ul>
<h3 id="dynamic-route-matching">[Dynamic Route Matching]</h3>
<p><em>파일 경로 ==&gt; router/index.js</em>
<img src="https://velog.velcdn.com/images/yoon-dh/post/2b212fdb-965b-47e6-b7af-2876b019784e/image.png" alt=""></p>
<p><em>파일 경로 ==&gt; HellowView.vue</em>
<img src="https://velog.velcdn.com/images/yoon-dh/post/e67421e2-4ca4-4187-8146-3faa09541201/image.png" alt=""></p>
<ul>
<li>HelloView.vue 작성 및 route를 추가</li>
<li>route를 추가할 때 동적 인자를 명시</li>
</ul>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/b02e9fe1-39c4-4664-9c27-cd31843676ad/image.png" alt=""></p>
<ul>
<li>$route.params 로 변수에 접근 가능</li>
<li>HTML에서 직접 사용하기보다는 data에 넣어서 사용하는 것을 권장 </li>
</ul>
<h4 id="선언적-방식-네비게이션">[선언적 방식 네비게이션]</h4>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/382c55f7-15ed-4e28-9ef1-0ca73e3d13da/image.png" alt=""></p>
<ul>
<li>params를 이용하여 동적 인자 전달 가능</li>
</ul>
<h4 id="프로그래밍-방식-네비게이션">[프로그래밍 방식 네비게이션]</h4>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/42fbf6f5-4532-438c-9a71-54bb1bb8c2b8/image.png" alt=""></p>
<ul>
<li>AboutView에서 데이터를 입력 받아 HelloView로 이동하여 입력받은 데이터에게 인사하기</li>
</ul>
<h3 id="route에-컴포넌트를-등록하는-또다른-방법">[route에 컴포넌트를 등록하는 또다른 방법]</h3>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/64972858-7f43-4400-aee7-12bca193b23b/image.png" alt=""></p>
<ul>
<li>route/index.js에 컴포넌트를 등록하는 또 다른 방식이 주어지고 있음(about)</li>
<li>lazy loading<ul>
<li>모든 파일을 한번에 로드하려고 하면 모든 걸 다 읽는 시간이 매우 오래 걸림</li>
<li>미리 로드를 하지 않고 특정 라우트에 방문할 때 매핑된 컴포넌트의 코드를 로드하는 방식을 활용할 수 있음 </li>
<li>모든 파일을 한 번에 로드하지 않아도 되기 때문에 최초에 로드하는 시간이 빨라짐</li>
<li>당장 사용하지 않을 컴포넌트는 먼저 로드하지 않는 것이 핵심 </li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Vue State Management]]></title>
            <link>https://velog.io/@yoon-dh/Vue-State-Management</link>
            <guid>https://velog.io/@yoon-dh/Vue-State-Management</guid>
            <pubDate>Thu, 04 May 2023 05:51:41 GMT</pubDate>
            <description><![CDATA[<h1 id="state-management">[State Management]</h1>
<ul>
<li><p>상태(state) : 현재에 대한 정보(data)</p>
</li>
<li><p>Web Application에서의 상태 ==&gt; 현재 App이 가지고 있는 Data로 표현 가능</p>
</li>
<li><p>여러개의 component를 조합해서 하나의 App을 만들고 있음</p>
</li>
<li><p>각 component는 독립적이기 때문에 각각의 상태를 가짐</p>
</li>
<li><p>하지만 결국 이러한 component들이 모여서 하나의 App을 구성할 예정 ==&gt; 즉, <strong>여러개의 component가 같은 상태(data)를 유지할 필요가 있음(상태 관리)</strong></p>
</li>
</ul>
<h2 id="pass-props--emit-event">[Pass Props &amp; Emit Event]</h2>
<ul>
<li>이전까지 props와 event를 이용해 상태 관리를 하고 있음</li>
<li>각 컴포넌트는 독립적으로 데이터를 관리</li>
<li>같은 데이터를 공유하고 있으므로, 각 컴포넌트가 동일한 상태를 유지하고 있음</li>
<li>데이터의 흐름을 직관적으로 파악 가능 </li>
<li><strong>But, component의 중첩이 깊어지면 데이터 전달이 쉽지 않음</strong></li>
<li>공통의 상태를 유지해야하는 component가 많아지면 데이터 전달 구조가 복잡해짐</li>
</ul>
<h2 id="vuexcentralized-store">Vuex(Centralized Store)</h2>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/c2a38d64-f6d2-4c4f-b989-cc7146e55a34/image.png" alt=""></p>
<ul>
<li>중앙 저장소(store)에 데이터를 모아서 상태 관리</li>
<li>각 component는 중앙 저장소의 데이터를 사용</li>
<li>component의 계층에 상관없이 중앙 저장소에 접근해서 데이터를 얻거나 변경할 수 있음</li>
<li>중앙 저장소의 데이터가 변경되면 각각의 component는 해당 데이터의 변화에 반응하여 새로 변경된 데이터를 반영 </li>
<li>규모가 크거나 컴포넌트 중첩이 깊은 프로젝트의 관리가 매우 편리</li>
</ul>
<p>==&gt; 위와 같은 중앙 저장소를 통한 상태관리를 도와주는 것이 <strong>Vuex</strong> / 데이터가 예측 가능한 방식으로만 변경될 수 있도록하는 규칙을 설정하며, Vue의 반응성을 효율적으로 사용하는 상태 관리 기능을 제공</p>
<h1 id="vuex">[Vuex]</h1>
<pre><code>$ vue create vuex-app</code></pre><ul>
<li>vue project 생성<pre><code>$ cd vuex-app</code></pre></li>
<li>디렉토리 이동<pre><code>$ vue add vuex </code></pre></li>
<li>Vue CLI를 통해 vuex plugin 적용</li>
<li>src / store / index.js 가 생성</li>
</ul>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/e37bbed2-19b2-4a63-9da4-dba1400c506b/image.png" alt=""></p>
<ul>
<li>vuex의 핵심 컨셉 4가지 </li>
</ul>
<ol>
<li>state</li>
<li>getters</li>
<li>mutations</li>
<li>actions</li>
</ol>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/0154e3f6-4087-4b5d-a7b1-eaa3281d4616/image.png" alt=""></p>
<h2 id="1-state">1. State</h2>
<ul>
<li>vue instance의 data에 해당</li>
<li><strong>중앙에서 관리하는 모든 상태 정보</strong></li>
<li>개별 component는 state에서 데이터를 가져와서 사용<ul>
<li>개별 component가 관리하던 data를 중앙 저장소(Vuex Store의 state)에서 관리하게 됨 </li>
</ul>
</li>
<li>state의 데이터가 변화하면 해당 데이터를 사용(공유)하는 component도 자동으로 다시 렌더링</li>
<li><strong>$store.state</strong>로 state 데이터에 접근</li>
</ul>
<h2 id="2-mutations">2. Mutations</h2>
<ul>
<li><strong>실제로 state를 변경하는 유일한 방법</strong> </li>
<li>vue instance의 methods에 해당하지만 Mutation에서 호출되는 핸들러 함수는 반드시 <strong>동기적</strong>이어야 함<ul>
<li>비동기 로직으로 mutations를 사용해서 state를 변경하는 경우, state의 변화의 시기를 특정할 수 없기 때문</li>
</ul>
</li>
<li>첫번째 인자로 <strong>state</strong>를 받으며, component 혹은 Actions에서 <strong>commit()</strong> 메서드로 호출됨</li>
</ul>
<h2 id="3-actions">3. Actions</h2>
<ul>
<li>mutations와 비슷하지만 <strong>비동기</strong> 작업을 포함할 수 있다는 차이가 있음</li>
<li><strong>state를 직접 변경하지 않고 commit() 메서드로 mutations를 호출해서 state를 변경함</strong></li>
<li>context 객체를 인자로 받으며, 이 객체를 통해 store.js의 모든 요소와 메서드에 접근할 수 있음( == 즉 state를 직접 변경할 수 있지만 하지 않아야 함)</li>
<li>component에서 <strong>dispatch()</strong> 메서드에 의해 호출됨</li>
</ul>
<p><strong>[Mutations &amp; Actions]</strong></p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/3512453c-f70b-4710-a5c5-ba53338ec4a8/image.png" alt=""></p>
<ul>
<li>Vue component의 methods 역할이 vuex에서는 아래와 같이 분화됨 </li>
<li>Mutations : state를 변경 </li>
<li>Actions : state 변경을 제외한 나머지 로직</li>
</ul>
<h2 id="4-getters">4. Getters</h2>
<ul>
<li>vue instance의 computed에 해당</li>
<li><strong>state를 활용하여 계산된 값을 얻고자 할 때 사용</strong></li>
<li>state의 원본 데이터를 건들지 않고 계산된 값을 얻을 수 있음</li>
<li>computed와 마찬가지로 getters의 결과는 캐시(cache)되며, 종속된 값이 변경된 경우에만 재계산됨</li>
<li>getters에서 계산된 값은 state에 영향을 미치지 않음</li>
<li>첫번째 인자로 <strong>state</strong>, 두번째 인자로 <strong>getter</strong>를 받음</li>
</ul>
<p><em><strong>[모든 데이터를 Vuex에서 관리해야 하는가?]</strong></em></p>
<ul>
<li>Vuex를 사용한다고 해서 모든 데이터를 state에 넣어야 하는 것은 아님</li>
<li>Vuex에서도 여전히 pass props, emit event를 사용하여 상태를 관리할 수 있음</li>
<li>개발 환경에 따라 적절하게 사용하는 것이 필요함</li>
</ul>
<p><em><strong>[summary]</strong></em></p>
<ul>
<li>state : 중앙에서 관리하는 모든 상태 정보</li>
<li>mutations : state를 변경하기 위한 methods</li>
<li>actions : 비동기 작업이 포함될 수 있는(외부 API와의 소통 등) methods / state를 변경하는 것 외의 모든 로직 진행 </li>
<li>getters: state를 활용해 계산한 새로운 변수 값</li>
</ul>
<p>component에서 데이터를 사용하기 위한 데이터의 흐름</p>
<ul>
<li>component ==&gt; (actions) ==&gt; mutations ==&gt; state</li>
</ul>
<p>component에서 데이터를 사용하기 위한 데이터의 흐름</p>
<ul>
<li>state ==&gt; (getters) ==&gt; component</li>
</ul>
<h1 id="vuex-example">[Vuex example]</h1>
]]></description>
        </item>
        <item>
            <title><![CDATA[Vue Data Management]]></title>
            <link>https://velog.io/@yoon-dh/Vue-Data-Management</link>
            <guid>https://velog.io/@yoon-dh/Vue-Data-Management</guid>
            <pubDate>Wed, 03 May 2023 04:56:05 GMT</pubDate>
            <description><![CDATA[<h1 id="vue-data-management">Vue Data Management</h1>
<h2 id="data-in-components">[Data in components]</h2>
<ul>
<li><p>우리는 정적인 웹페이지가 아닌 동적 웹을 만든다. </p>
</li>
<li><p>다루어야 할 데이터들은 한 페이지 내에서 같은 데이터를 공유하기도 하는데 페이지들은 각각의 component로 구분이 되어있음</p>
</li>
<li><p>완전히 동일한 데이터를 서로 다른 component에서 보여주려면 어떻게 해야할까?</p>
<ul>
<li>필요한 컴포넌트끼리 데이터를 주고받기 ( X )<ul>
<li>데이터 흐름을 파악하기 힘들다</li>
<li>개발 속도 저하</li>
<li>유지보수 난이도 증가 </li>
</ul>
</li>
<li><strong>컴포넌트의 부모 - 자식 관계만 데이터를 주고 받도록</strong> ( O )<ul>
<li>데이터 흐름 파악 용이</li>
<li>유지 보수 용이</li>
<li>부모 ==&gt; 자식 으로의 데이터 흐름(pass <strong>props</strong>)</li>
<li>자식 ==&gt; 부모로의 데이터 흐름 (<strong>emit</strong> event)</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="pass-props">[Pass Props]</h2>
<ul>
<li>요소의 속성을 사용하여 데이터 전달</li>
<li>props는 부모(상위) 컴포넌트의 정보를 전달하기 위한 사용자 지정 특성</li>
<li>자식(하위) 컴포넌트는 props 옵션을 사용하여 수신하는 props를 명시적으로 선언해야 함</li>
<li>정적인 데이터를 전달하는 경우 static props라고 명시하기도 함</li>
<li>요소에 속성을 작성하듯이 사용 가능하여, prop-data-name = &quot;value&quot;의 형태로 데이터를 전달(이때 속성의 키 값은 kebab-case를 사용)</li>
</ul>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/8ea4ef7b-186e-4910-ad1a-40e84ea2d996/image.png" alt=""></p>
<ul>
<li>prop 명시</li>
<li>데이터를 받는 쪽, 즉 하위 컴포넌트에서도 props에 대해 명시적으로 작성해주어야 함</li>
<li>전달받은 props를 type과 함께 명시</li>
<li>컴포넌트를 문서화할 뿐만 아니라, 잘못된 타입이 전달하는 경우 브라우저의 자바스크립트 콘솔에서 사용자에게 경고</li>
</ul>
<p><strong>[Pass Props convention]</strong></p>
<ul>
<li>부모에서 넘겨주는 props <ul>
<li>kebab-case(HTML 속성명은 대소문자를 구분하지 않기 때문)</li>
</ul>
</li>
<li>자식에서 받는 props<ul>
<li>camelCase</li>
</ul>
</li>
<li>부모 템플릿(html)에서 kebab-case로 넘긴 변수를 자식의 스크립트(vue)에서 자동으로 camelCase로 변환하여 인식함</li>
</ul>
<p><strong>[Dynamic props]</strong></p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/084cb8b1-dcfe-407d-afe2-6906e3af10c4/image.png" alt="">
<img src="https://velog.velcdn.com/images/yoon-dh/post/984d8dbf-44ef-45b3-8c07-baccca576795/image.png" alt=""></p>
<ul>
<li><p>변수를 props로 전달할 수 있음</p>
</li>
<li><p>v-bind directive를 사용해 데이터를 동적으로 바인딩</p>
</li>
<li><p>부모 컴포넌트의 데이터가 업데이트되면 자식 컴포넌트로 전달되는 데이터 또한 업데이트 됨</p>
</li>
<li><p>:dynamic-props=&quot;dynamicProps&quot;는 
앞의 key값(dynamic-props)이란 이름으로 뒤의 &quot; &quot;안에 오는 데이터(dynamicProps)를 전달하겠다는 뜻</p>
</li>
<li><p>즉, :my-props=&quot;dynamicProps&quot;로 데이터를 넘긴다면, 자식 컴포넌트에서 myProps로 데이터를 받아야 함</p>
</li>
<li><p>v-bind로 묶여있는 &quot; &quot; 안의 구문은 JS의 구문으로 볼 수 있음 (따라서 dynamicProps라고 하는 변수에 대한 data를 전달할 수 있는 것)</p>
</li>
</ul>
<p><strong>[단방향 데이터 흐름]</strong></p>
<ul>
<li>모든 props는 부모에서 자식으로 즉 아래로 단방향 바인딩을 형성</li>
<li>부모 속성이 업데이트 되면 자식으로 흐르지만 반대 방향은 아님<ul>
<li>부모 컴포넌트가 업데이트 될 때마다 자식 컴포넌트의 모든 prop들이 최신값으로 새로고침 됨</li>
<li>하위 컴포넌트가 실수로 상위 컴포넌트 상태를 변경하여 앱의 데이터 흐름을 이해하기 힘들게 만드는 것을 방지</li>
<li>하위 컴포넌트에서 prop을 변경하려고 시도해서는 안되며 그렇게 하면 Vue는 콘솔에서 경고를 출력함</li>
</ul>
</li>
</ul>
<h2 id="emit-event">[Emit Event]</h2>
<ul>
<li>부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달할 때는 이벤트를 발생시킴</li>
<li>이벤트를 발생시킴으로 어떻게 데이터를 전달?<ul>
<li>데이터를 이벤트 리스너의 <strong>콜백함수의 인자로 전달</strong></li>
<li>상위 컴포넌트는 *<em>해당 이벤트를 통해 데이터를 받음 *</em></li>
</ul>
</li>
</ul>
<h3 id="emit">[$emit]</h3>
<ul>
<li>$emit 메서드를 통해 부모 컴포넌트에 이벤트를 발생<ul>
<li>$emit(&#39;event-name&#39;) 형식으로 사용하며 부모 컴포넌트에 event-name이라는 이벤트가 발생했다는 것을 알림 </li>
<li>마치 사용자가 마우스 클릭을 하면 click event가 발생하는 것처럼 $emit(&#39;event-name&#39;)가 실행되면 event-name 이벤트가 발생하는 것</li>
</ul>
</li>
</ul>
<p><strong>[Emit Event]</strong></p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/f1a1b580-f9ca-498b-9bdb-61eca6a27d37/image.png" alt=""></p>
<ol>
<li>자식 컴포넌트에 버튼을 만들고 클릭 이벤트를 추가</li>
<li>$emit을 통해 부모 컴포넌트에게 child-to-parent 이벤트를 트리거</li>
</ol>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/408f97a5-d3e4-4c08-acb1-6dc21352d6af/image.png" alt=""></p>
<ol start="3">
<li>emit된 이벤트를 상위 컴포넌트에서 청취 후 핸들러 함수 실행 </li>
</ol>
<ul>
<li>자식 컴포넌트에 있는 버튼 클릭 이벤트를 청취하여 연결된 핸들러 함수 호출</li>
<li>호출된 함수에서 $emit을 통해 상위 컴포넌트에 이벤트 발생</li>
<li>상위 컴포넌트는 자식 컴포넌트가 발생시킨 이벤트를 청취하여 연결된 핸들러 함수 호출 </li>
</ul>
<p><strong>[emit with data]</strong></p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/5b551c5f-5992-4a0a-930b-b1bff8185ef9/image.png" alt=""></p>
<ul>
<li>이벤트 발생 시 인자로 데이터를 전달 가능</li>
</ul>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/6f1c482a-5e16-4df9-a625-9438b514fe90/image.png" alt=""></p>
<ul>
<li>이렇게 전달한 데이터는 이벤트와 연결된 부모 컴포넌트의 핸들러 함수의 인자로 사용 가능 </li>
</ul>
<ol>
<li>자식 컴포넌트에 있는 버튼 클릭 이벤트를 청취하여 연결된 핸들러 함수 호출</li>
<li>호출된  함수에서 $emit을 통해 부모 컴포넌트에 이벤트를 발생(이벤트에 데이터를 함께 전달)</li>
<li>부모 컴포넌트는 자식 컴포넌트의 이벤트를 청취하여 연결된 핸들러 함수 호출, 함수의 인자로 전달된 데이터가 포함되어 있음</li>
</ol>
<p><strong>[emit with dynamic data]</strong></p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/f4318af0-3dcd-4d2b-9ae4-b86d374ffd51/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/c99c90a3-6bb1-44ae-9c6e-655e70c786c8/image.png" alt=""></p>
<h2 id="pass-props--emit-event-컨벤션">[pass props / emit event 컨벤션]</h2>
<ul>
<li>HTML 요소에서 사용할 때는 kebab-case</li>
<li>JS에서 사용할 때는 camelCase</li>
<li><strong>props</strong><ul>
<li>상위 ==&gt; 하위 흐름에서 HTML 요소로 내려줌 : kebab-case</li>
<li>하위에서 받을 때 JS에서 받음 camelCase</li>
</ul>
</li>
<li><strong>emit</strong> <ul>
<li>emit 이벤트를 발생시키면 HTMl요소가 이벤트를 청취 : kebab-case</li>
<li>메서드, 변수명 등은 JS에서 사용함 : camelCase</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Vue Component]]></title>
            <link>https://velog.io/@yoon-dh/Vue-Component</link>
            <guid>https://velog.io/@yoon-dh/Vue-Component</guid>
            <pubDate>Tue, 02 May 2023 06:19:05 GMT</pubDate>
            <description><![CDATA[<h1 id="component">[Component]</h1>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/180f705b-5adf-40bb-b31b-16eb00a95d5d/image.png" alt=""></p>
<ul>
<li>** 유지/보수 용이, 재사용성, 확장 가능, 캡슐화, 독립적**</li>
<li>UI를 독립적이고 재사용 가능한 조각들로 나눈 것(기능별로 분화한 코드 조각)</li>
<li>CS에서는 다시 사용할 수 있는 범용성을 위해 개발된 소프트웨어 구성 요소를 의미</li>
<li>하나의 app을 구성할 때 중첩된 컴포넌트들의 tree로 구성하는 것이 보편적임<ul>
<li>src/App.vue를 root node로 하는 tree의 구조를 가짐</li>
</ul>
</li>
<li>컴포넌트는 유지보수를 쉽게 만들어 줄 뿐만 아니라 재사용성의 측면에서도 매우 강력한 기능을 제공 </li>
<li>우리가 사용하는 웹 서비스는 여러개의 컴포넌트로 이루어져 있음</li>
<li>하나의 컴포넌트를 만들어두면 반복되는 UI를 쉽게 처리할 수 있음</li>
</ul>
<h1 id="sfc">[SFC]</h1>
<ul>
<li>component in Vue<ul>
<li>이름이 있는 재사용 가능한 Vue instance</li>
<li>new Vue()로 만든 인스턴스</li>
</ul>
</li>
</ul>
<p><strong>[SFC(Single File Component)]</strong></p>
<ul>
<li>하나의 .vue 파일이 하나의 Vue instance이고, 하나의 컴포넌트이다.</li>
<li>Vue instance에서는 HTML,CSS,JavaScript 코드를 한번에 관리</li>
<li>컴포넌트 기반 개발의 핵심 기능 </li>
<li>Vue CLI가 Vue를 Component based하게 사용가능하게 함</li>
</ul>
<h1 id="vue-component">[Vue Component]</h1>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/c99c60ed-1f72-4d57-ad9d-c03ec7cf7ffc/image.png" alt=""></p>
<ul>
<li>템플릿(HTML)<ul>
<li>HTML의 body 부분</li>
<li>눈으로 보여지는 요소 작성</li>
<li>다른 컴포넌트를 HTML 요소처럼 추가 가능</li>
</ul>
</li>
<li>스크립트(JavaScript)<ul>
<li>JS 코드가 작성되는 곳</li>
<li>컴포넌트 정보, 데이터, 메서드 등 Vue instance를 구성하는 대부분이 작성 됨</li>
</ul>
</li>
<li>스타일(CSS)<ul>
<li>CSS가 작성되며 컴포넌트의 스타일을 담당</li>
</ul>
</li>
</ul>
<ul>
<li>컴포넌트들이 tree 구조를 이루어 하나의 페이지를 만듦</li>
<li>root에 해당하는 최상단의 component가 App.vue</li>
<li>이 App.vue를 index.html 과 연결 </li>
<li>결국 index.html 파일 하나만을 rendering(SPA) </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Vue CLI]]></title>
            <link>https://velog.io/@yoon-dh/Vue-CLI</link>
            <guid>https://velog.io/@yoon-dh/Vue-CLI</guid>
            <pubDate>Tue, 02 May 2023 05:57:03 GMT</pubDate>
            <description><![CDATA[<h1 id="vue-cli">Vue CLI</h1>
<h2 id="vue-cli-install">[Vue CLI install]</h2>
<ul>
<li>Vue 개발을 위한 표준 도구</li>
<li>프로젝트의 구성을 도와주는 역할</li>
<li>확장 플러그인, GUI, Babel 등 다양한 tool 제공</li>
</ul>
<p><strong>[설치]</strong></p>
<pre><code>$ npm install -g @vue/cli</code></pre><p><strong>[프로젝트 생성]</strong></p>
<pre><code>$ vue create vue-cli</code></pre><ul>
<li>vscode terminal에서 진행</li>
</ul>
<p><strong>[Vue 버전 선택]</strong></p>
<pre><code>&gt; Default ([Vue 2] babel,eslint)</code></pre><p><strong>[설치 시 출력되는 명령어 실행]</strong></p>
<pre><code>$ cd vue-cli

$ npm run serve </code></pre><h2 id="vue-cli-프로젝트-구조">[Vue CLI 프로젝트 구조]</h2>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/1c7295b6-e6b9-4fb4-892a-1179e2bffb0d/image.png" alt=""></p>
<h3 id="node-modules">[node-modules]</h3>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/f41f22a8-743e-424d-910d-c24a53d95985/image.png" alt=""></p>
<ul>
<li>node.js 환경의 여러 의존성 모듈</li>
<li>python의 venv와 비슷한 역할을 함<ul>
<li>따라서 .gitignore에 넣어줘야 하며, Vue 프로젝트를 생성하면 자동으로 추가 됨</li>
</ul>
</li>
</ul>
<h4 id="babel">[Babel]</h4>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/f8b0d2f6-b9c9-4c8f-9248-6e9763fa33f3/image.png" alt=""></p>
<ul>
<li>JS Compiler</li>
<li>JS의 ES6+ 코드를 구버전으로 번역/변환 해주는 도구</li>
<li>JS의 파편화, 표준화의 영향으로 작성된 코드의 스펙트럼이 매우 다양<ul>
<li>최신 문법을 사용해도 브라우저의 버전 별로 동작하지 않는 상황이 발생</li>
<li>버전에 따른 같은 의미의 다른 코드를 작성하는 등의 대응이 필요해졌고, 이러한 문제를 해결하기 위한 도구</li>
<li>원시 코드(최신 버전)를 목적 코드(구 버전)으로 옮기는 번역기가 등장하면서 더 이상 코드가 특정 브라우저에서 동작하지 않는 상황에 대해 크게 고민하지 않을 수 있음</li>
</ul>
</li>
</ul>
<h4 id="webpack">[Webpack]</h4>
<ul>
<li>static module bundler</li>
<li>모듈 간의 의존성 문제를 해결하기 위한 도구</li>
<li>프로젝트에 필요한 모든 모듈을 매핑하고, 내부적으로 종속성 그래프를 빌드함</li>
</ul>
<p><em>[Module]</em></p>
<ul>
<li>개발하는 어플리케이션의 크기가 커지고 복잡해지면, 파일 하나에 모든 기능을 담기가 어려워짐</li>
<li>따라서 자연스럽게 파일을 여러 개로 분리하여 관리를 하게 되었고, 이때 분리된 파일 각각이 모듈 즉, JS파일 하나가 하나의 모듈</li>
<li>모듈은 대개 기능 단위로 분리하며, 클래스 하나 혹은 특정한 목적을 가진 복수의 함수로 구성된 라이브러리 하나로 구성됨</li>
<li>** Module 의존성 문제** : 모듈의 수가 많아지고 라이브러리 혹은 모듈 간의 의존성이 깊어지면서 특정한 곳에서 발생한 문제가 어떤 모듈 간의 문제인지 파악하기 어려움 ==&gt; <strong>Webpack은 이 모듈 간의 의존성 문제를 해결하기 위해 등장</strong></li>
</ul>
<p><em>[Bundler]</em></p>
<ul>
<li>모듈 의존성 문제를 해결해주는 작업이 Bundling</li>
<li>이러한 일을 해주는 도구가 Bundler이고, <strong>Webpack은 다양한 Bundler 중 하나</strong></li>
<li>모듈들을 하나로 묶어주고 묶인 파일은 하나(혹은 여러개)로 만들어짐</li>
<li>Bundling된 결과물은 개별 모듈의 실행 순서에 영향을 받지않고 동작하게 됨</li>
<li><strong>Vue CLI는 이러한 Babel,Webpack에 대한 초기 설정이 자동으로 되어있음</strong></li>
</ul>
<h3 id="packagejson">[package.json]</h3>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/2cecdaae-1340-4472-8029-818d452f7bf0/image.png" alt=""></p>
<ul>
<li>프로젝트의 종속성 목록과 지원되는 브라우저에 대한 구성 옵션을 포함</li>
</ul>
<h3 id="package-lockjson">[package-lock.json]</h3>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/35f1e917-9cff-403b-86e3-23dc315fef1c/image.png" alt=""></p>
<ul>
<li>node_modules에 설치되는 모듈과 관련된 모든 의존성을 설정 및 관리</li>
<li>협업 및 배포 환경에서 정확히 동일한 종속성을 설치하도록 보장하는 표현</li>
<li>사용할 패키지의 버전을 고정 </li>
<li>개발 과정 간의 의존성 패키지 충돌 방지</li>
<li>python의 requirement.txt 역할 </li>
</ul>
<h3 id="publicindexhtml">[public/index.html]</h3>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/fa1fe4ee-2dd5-4d70-bcab-cdf4234d91eb/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/422ea6e0-3476-4975-b60d-072e4e2dc379/image.png" alt=""></p>
<ul>
<li>Vue 앱의 뼈대가 되는 html파일</li>
<li>Vue 앱과 연결될 요소가 있음</li>
</ul>
<h3 id="src">[src/]</h3>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/5e94b5d2-b419-4f8f-afde-34332bda7d2b/image.png" alt=""></p>
<ul>
<li>src/assets<ul>
<li>정적 파일을 저장하는 디렉토리</li>
</ul>
</li>
<li>src/components
  -하위 컴포넌트들이 위치</li>
<li>src/App.vue<ul>
<li>최상위 컴포넌트</li>
<li>public/index.html과 연결됨</li>
</ul>
</li>
<li>src/main.js<ul>
<li>webpack이 빌드를 시작할 때 가장 먼저 불러오는 entry point</li>
<li>public/index.html과 src/App.vue를 연결시키는 작업이 이루어지는 곳</li>
<li>Vue 전역에서 활용 할 모듈을 등록할 수 있는 파일</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Vue.js - syntax, Vue advanced]]></title>
            <link>https://velog.io/@yoon-dh/Vue.js-syntax-Vue-advanced</link>
            <guid>https://velog.io/@yoon-dh/Vue.js-syntax-Vue-advanced</guid>
            <pubDate>Mon, 01 May 2023 06:04:54 GMT</pubDate>
            <description><![CDATA[<h1 id="basic-of-syntax">[Basic of Syntax]</h1>
<h2 id="template-syntax">Template Syntax</h2>
<ul>
<li><p>렌더링 된 DOM을 기본 Vue instance의 data에 선언적으로 바인딩할 수 있는 HTMl 기반 template syntax를 사용 </p>
<ul>
<li>렌더링된 DOM : 브라우저에 의해 보기 좋게 그려질 HTML 코드</li>
<li>HTML 기반 template syntax : HTML 코드에 직접 작성할 수 있는 문법 제공</li>
<li>선언적으로 바인딩: Vue instance와 DOM을 연결</li>
</ul>
</li>
</ul>
<h3 id="text-interpolation">Text Interpolation</h3>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/c59bff14-0262-45ce-972d-7fe3d7bf9fab/image.png" alt=""></p>
<ul>
<li>가장 기본적인 바인딩(연결) 방법</li>
<li>중괄호 2개로 표기</li>
<li>DTL과 동일한 형태로 작성</li>
<li>Text interpolation 방법은 모두 일반 텍스트로 표현</li>
</ul>
<h3 id="raw-html">Raw HTML</h3>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/86e48a5d-31b5-4a85-8d62-e8fc8b6535c0/image.png" alt=""></p>
<ul>
<li>v-html directive를 사용하여 data와 바인딩</li>
<li>directive - HTML 기반 template syntax</li>
<li>HTML의 기본속성이 아닌 Vue가 제공하는 특수 속성의 값으로 data를 작성</li>
</ul>
<h1 id="directives">[Directives]</h1>
<ul>
<li>v-접두사가 있는 특수 속성에는 값을 할당 할 수 있음(값에는 JS 표현식을 작성할 수 있음)</li>
<li>directive의 역할은 표현식의 값이 변경될 때 반응적으로 Dom에 적용하는 것</li>
</ul>
<h2 id="directives-기본-구성">Directives 기본 구성</h2>
<p>[월요일 vue 교재 10p 사진 추가]</p>
<ul>
<li>&#39; <strong>:</strong> &#39; 을 통해 전달 인자를 받을 수 있음 </li>
<li>&#39; <strong>.</strong> &#39; 으로 표시되는 특수 접미사 - directive를 특별한 방법으로 바인딩 해야 함</li>
</ul>
<h3 id="v-text">[v-text]</h3>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/3fe9ddfc-9882-4baa-bb39-c31ae2474ac5/image.png" alt=""></p>
<ul>
<li>Template Interpolation과 함께 가장 기본적인 바인딩 방법</li>
<li>{{ }} 와 동일한 역할(정확히 동일한 역할인 것은 아님)</li>
</ul>
<h3 id="v-html">[v-html]</h3>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/dea49eb9-a76b-47b1-8a36-730787bd7130/image.png" alt=""></p>
<ul>
<li>RAW HTML을 표현할 수 있는 방법</li>
<li>단, 사용자가 입력하거나 제공하는 컨텐츠에는 절대 사용 금지(XSS 공격 참고)</li>
</ul>
<h3 id="v-show">[v-show]</h3>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/8a0e9e08-8daa-48c4-90ea-fc5aabe32533/image.png" alt=""></p>
<ul>
<li>표현식에 작성된 값에 따라 element를 보여줄 것인지 결정</li>
<li><strong>boolean</strong> 값이 변경될 때 마다 반응</li>
<li>대상  element의 display 속성을 기본 속성과 none으로 toggle</li>
<li>요소 자체는 항상 DOM에 렌더링 됨</li>
<li>false값이 되어도 화면에서만 사라졌을 뿐, DOM에는 존재한다. (display 속성만 변경된 것)</li>
</ul>
<h3 id="v-if">[v-if]</h3>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/2a14cabe-8f07-423b-abba-5ecfcb98d478/image.png" alt=""></p>
<ul>
<li>v-show와 사용 방법은 동일 </li>
<li>isActive의 값이 변경 될 때 반응</li>
<li>단, 값이 false인 경우 <strong>DOM에서 사라짐</strong></li>
<li><strong>v-if, v-else-id, v-else</strong> 형태로 사용 </li>
</ul>
<h4 id="v-show-vs-v-if">v-show VS v-if</h4>
<ul>
<li><p>v-show(Expensive initial load, cheap toggle)</p>
<ul>
<li>표현식 결과와 관계없이 렌더링 되므로 초기 렌더링에 필요한 비용은 v-if보다 높을 수 있음</li>
<li>display 속성 변경으로 표현 여부를 판단하므로 렌더린 후 toggle 비용은 적음</li>
</ul>
</li>
<li><p>v-if (Cheap initial load, expensive toggle)</p>
<ul>
<li>표현식 결과가 false인 경우 렌더링조차 되지 않으므로 초기 렌더링 비용은 v-show 보다 낮을 수 있음</li>
<li>단, 표현식 값이 자주 변경되는 경우 잦은 재 렌더링 비용이 증가할 수 있음</li>
</ul>
</li>
</ul>
<h3 id="v-for">[v-for]</h3>
<h4 id="string">[string]</h4>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/48a5ec08-9e06-4a1f-b92a-332acb024b3b/image.png" alt=""></p>
<ul>
<li><strong>for .. in ..</strong> 형식으로 작성</li>
<li>반복한 데이터 타입에 모두 사용 가능</li>
<li>index를 함께 출력하고자 한다면 (char,index) 형태로 사용 가능</li>
</ul>
<h4 id="array">[Array]</h4>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/03414373-faf5-4394-ae90-01c151c79fcf/image.png" alt=""></p>
<ul>
<li>배열 역시 문자열과 동일하게 사용 가능</li>
<li>각 요소가 객체라면 dot notation으로 접근할 수 있음</li>
</ul>
<p><strong>[참고] 특수 속성 key</strong></p>
<ul>
<li>&quot;v-for 사용 시 반드시 key 속성을 각 요소에 작성&quot;</li>
<li>주로 v-for directive 작성 시 사용</li>
<li>vue 화면 구성 시 이전과 달라진 점을 확인하는 용도로 활용(따라서 key가 중복되어서는 안됨)</li>
<li>각 요소가 고유한 값을 가지고 있다면 생략할 수 있음</li>
</ul>
<h4 id="object">[Object]</h4>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/6219b670-b151-4b92-bae2-a45cdc8cbe1d/image.png" alt=""></p>
<ul>
<li>객체 순회 시 value가 할당되어 출력</li>
<li>두번째 변수 할당 시 key 출력 가능</li>
</ul>
<h3 id="v-on">[v-on]</h3>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/6444004f-cd71-42b5-b744-c0b1698a9c8c/image.png" alt=""></p>
<ul>
<li>&#39; : &#39; 을 통해 전달받은 인자를 확인 </li>
<li>값으로 JS 표현식 작성</li>
<li>addEventListener의 첫 번째 인자와 동일한 값들로 구성</li>
<li>대기하고 있던 이벤트가 발생하면 할당된 표현식 실행</li>
<li>methodfmf 통한 data 조작도 가능</li>
<li>method에 인자를 넘기는 방법은 일반 함수를 호출할 때와 동일한 방식</li>
<li>&#39;:&#39;을 통해 전달된 인자에 따라 특별한 modifiers(수식어)가 있을 수 있음<ul>
<li>ex) v-on:keyup.enter 등</li>
</ul>
</li>
<li>&#39;@&#39; shortcut 제공<ul>
<li>ex) @keyup.click</li>
</ul>
</li>
</ul>
<h3 id="v-if-1">[v-if]</h3>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/6e1ea187-9db1-45b5-9744-c03dafd9f217/image.png" alt=""></p>
<ul>
<li><p>HTML 기본 속성에 Vue data를 연결</p>
</li>
<li><p>class의 경우 다양한 형태로 연결 가능</p>
<ul>
<li>조건부 바인딩<ul>
<li>{&#39;class Name&#39;: &#39;조건 표현식&#39;}</li>
<li>삼항 연산자도 가능</li>
</ul>
</li>
<li>다중 바인딩<ul>
<li>[&#39;JS표현식&#39;,&#39;JS표현식&#39;...]</li>
</ul>
</li>
</ul>
</li>
<li><p>Vue data의 변화에 반응하여 DOM에 반영하므로 상황에 따라 유동적 할당 가능</p>
</li>
<li><p>&#39; : &#39; shortcut 제공</p>
<ul>
<li>ex) :class 등</li>
<li>v-for에서 사용하였던 :key는 v-bind의 숏컷을 활용한 것</li>
</ul>
</li>
</ul>
<h3 id="v-model">[v-model]</h3>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/41e86bfc-9677-4562-a1fa-46a0bae6ad4f/image.png" alt=""></p>
<ul>
<li>Vue instance와 DOM의 양방향 바인딩</li>
<li>Vue data 변경 시 v-model로 연결된 사용자 입력 element에도 적용</li>
</ul>
<h1 id="vue-advanced">[Vue advanced]</h1>
<h2 id="computed">computed</h2>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/451adc8f-0a18-49f3-af54-bf852f670b9b/image.png" alt=""></p>
<ul>
<li>vue instance가 가진 options 중 하나</li>
<li>computed 객체에 정의한 함수를 페이지가 최초로 렌더링될 때 호출하여 계산</li>
<li>계산 결과가 변하기 전까지 함수를 재호출하는 것이 아닌 계산된 값을 반환</li>
</ul>
<h3 id="methods-vs-computed">methods VS computed</h3>
<ul>
<li>methods<ul>
<li>호출될 때마다 함수를 실행</li>
<li>같은 결과여도 매번 새롭게 계산</li>
</ul>
</li>
<li>computed<ul>
<li>함수의 종속 대상의 변화에 따라 계산 여부가 결정 됨</li>
<li>종속 대상이 변하지 않으면 항상 저장(캐싱)된 값을 변환</li>
</ul>
</li>
</ul>
<h2 id="watch">watch</h2>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/489eec5b-f3ba-4911-8063-d39ef585d5e1/image.png" alt=""></p>
<ul>
<li>특정데이터의 변화를 감지하는 기능<ol>
<li>watch 객체를 정의</li>
<li>감시할 대상 data를 지정</li>
<li>data가 변할 시 실행 할 함수를 정의</li>
</ol>
</li>
<li>첫 번째 인자는 변동 후 data</li>
<li>두 번째 인자는 변동 전 data</li>
<li>실행 함수를 Vue method로 대체 가능<ol>
<li>감시대상 data의 이름으로 객체 생성</li>
<li>실행하고자 하는 method를 handler에 문자열 형태로 할당</li>
</ol>
</li>
<li>Array,Object 내부 요소 변경 감지를 위해서는 deep 속성필요</li>
</ul>
<h2 id="filter">filter</h2>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/8c6b1bed-9ff7-4a96-bf45-64e23eb37d81/image.png" alt=""></p>
<ul>
<li>텍스트 형식화를 적용할 수 있는 필터</li>
<li>interpolation 혹은 v-bind를 이용할 때 사용 가능</li>
<li>필터는 자바스트립트 표현식 마지막에 &#39; | &#39; (파이프)와 함께 추가 되어야 함 </li>
<li>이어서 사용(chaining) 가능</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Vue.js]]></title>
            <link>https://velog.io/@yoon-dh/Vue.js</link>
            <guid>https://velog.io/@yoon-dh/Vue.js</guid>
            <pubDate>Mon, 01 May 2023 04:40:57 GMT</pubDate>
            <description><![CDATA[<h2 id="mvvm-pattern">MVVM Pattern</h2>
<ul>
<li><p>소프트웨어 아키텍처 패턴의 일종 </p>
</li>
<li><p>마크업 언어로 구현하는 그래픽 사용자 인터페이스(view)의 개발을 Back-end(model)로부터 분리시켜 view가 어느 특정한 모델 플랫폼에 종속되지 않도록 함</p>
</li>
<li><p><strong>View</strong> : 우리 눈에 보이는 부분 = DOM</p>
</li>
<li><p><strong>Model</strong> : 실제 데이터 = JSON</p>
</li>
<li><p><strong>Veiw Model(vue)</strong></p>
<ul>
<li>View를 위한 Model</li>
<li>View와 연결(binding)되어 Action을 주고 받음 </li>
<li>Model이 변경되면 View Model도 변경되고 바인딩된 View도 변경됨</li>
<li>View Model의 데이터가 변경되고 바인딩 된 다른 View도 변경됨</li>
</ul>
</li>
</ul>
<h2 id="view-instance">View instance</h2>
<ul>
<li>Vue instance === 1개의 객체</li>
<li>아주 많은 속성과 메서드를 이미 가지고 있고, 이러한 기능들을 사용하는 것</li>
</ul>
<h3 id="el-element">el (element)</h3>
<ul>
<li>Vue instance 와 DOM을 연결(mount)하는 옵션<ul>
<li>HTML id 혹은 class와 마운트 역할 </li>
<li>View와 Model을 연결하는 역할 </li>
</ul>
</li>
<li>Vue instance와 연결되지 않은 DOM 외부는 Vue의 영향을 받지 않음(Vue 속성 및 메서드 사용 불가)</li>
</ul>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/2903cf33-6ffc-44f0-af0f-14f539eb6865/image.png" alt=""></p>
<ul>
<li>새로운 Vue instance 생성 </li>
<li>생성자 함수 첫번째 인자로 Object 작성</li>
<li>el 옵션에 #app 작성 = DOM 연결 </li>
</ul>
<h3 id="data">data</h3>
<ul>
<li>Vue instance의 데이터 객체 혹은 인스턴스 속성</li>
<li>데이터 객체는 반드시 기본 객체 {} (Object)여야 함</li>
<li>객체 내부의 아이템들은 value로 모든 타입의 객체를 가질 수 있음</li>
<li>정의된 속성은 interpolation {{}} 을 통해 view에 렌더링 가능함</li>
</ul>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/c5c5d0ca-b705-4eb3-ad64-c4b75431fa1b/image.png" alt=""></p>
<ul>
<li>Vue instance에 data 객체 추가</li>
<li>data 객체에 message 값 추가</li>
<li>추가된 객체의 각 값들은 this.message 형태로 접근 가능</li>
</ul>
<h3 id="methods">methods</h3>
<ul>
<li>Vue instance의 method들을 정의하는 곳</li>
<li>methods 객체 정의</li>
</ul>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/c601d0f8-90c4-4494-bdac-0d759f605cf2/image.png" alt=""></p>
<p><strong>[주의] methods with Arrow Function</strong></p>
<ul>
<li>메서드를 정의할 때, Arrow Function을 사용하면 안됨</li>
<li>Arrow Function의 this는 함수가 선언될 때 상위 스코프를 가리킴</li>
<li>즉 this가 상위 객체 window를 가리킴</li>
<li>호출은 문제없이 가능하나 this로 Vue의 data를 변경하지 못함 </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Django REST framework(DRF) - Comment(N:1)]]></title>
            <link>https://velog.io/@yoon-dh/Django-REST-frameworkDRF-CommentN1</link>
            <guid>https://velog.io/@yoon-dh/Django-REST-frameworkDRF-CommentN1</guid>
            <pubDate>Mon, 17 Apr 2023 15:05:56 GMT</pubDate>
            <description><![CDATA[<ul>
<li>data를 serialization하여 JSON으로 변환하는 방법 학습</li>
</ul>
<h1 id="n1-참조-조회">[N:1 참조 조회]</h1>
<h2 id="실습-사전-준비">[실습 사전 준비]</h2>
<p>파일 경로 ➡️ articles/models.py</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/4c3a9165-a6af-4642-a975-985a8a2098ad/image.png" alt=""></p>
<pre><code>$ python manage.py makemigrations 

$ python manage.py migrate</code></pre><ul>
<li>Comment 모델 작성 및 데이터베이스 초기화 &amp; migration </li>
</ul>
<h2 id="get---list">GET - LIST</h2>
<ul>
<li>댓글 데이터 목록 조회하기</li>
</ul>
<p>파일 경로 ➡️ articles/serializers.py</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/794c4097-07b0-4295-97d6-da008a151f37/image.png" alt=""></p>
<p>파일 경로 ➡️ articles/urls.py</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/e5895fc0-3ac9-480b-808f-265344502ac6/image.png" alt=""></p>
<p>파일 경로 ➡️ articles/views.py</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/baf2f357-a552-49d3-beb7-9feb5f5cfd75/image.png" alt=""></p>
<h2 id="get--detail">GET = DETAIL</h2>
<ul>
<li>단일 댓글 데이터 조회하기</li>
<li>Article과 달리 같은 serializer 사용하기</li>
</ul>
<p>파일 경로 ➡️ articles/urls.py</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/a120271a-20d2-40f4-8160-1716f038c8e4/image.png" alt=""></p>
<p>파일 경로 ➡️ articles/views.py</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/3dc2244a-193a-4010-8699-078e735f5651/image.png" alt=""></p>
<h2 id="post">POST</h2>
<ul>
<li>단일 댓글 데이터 생성</li>
</ul>
<p>파일 경로 ➡️ articles/urls.py</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/40af4813-6e8a-44d5-9b10-9e5282c406fc/image.png" alt=""></p>
<p>파일 경로 ➡️ articles/views.py</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/7949ea19-d10d-4e2a-a407-f828c5a14695/image.png" alt=""></p>
<ul>
<li>line 6</li>
<li>save() 메서드는 특정 Serializer 인스턴스를 저장하는 과정에서 추가적인 데이터를 받을 수 있음</li>
<li>CommentSerializer를 통해 Serialize 되는 과정에서 parameter로 넘어온 article_pk에 해당하는 article 객체를 추가적인 데이터를 넘겨 저장</li>
</ul>
<p>파일 경로 ➡️ articles/serializers.py</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/1f53a61b-6d2d-4634-a2d4-ab9006d554cc/image.png" alt=""></p>
<ul>
<li>CommentSerializer에서 article field 데이터 또한 사용자로부터 입력받도록 설정되어있어 에러가 발생</li>
<li><strong>read_only_fields</strong>를 사용해 외래 키 필드를 읽기전용 필드로 설정 </li>
<li>읽기전용 필드는 데이터를 전송하는 시점에 <strong>&#39;해당필드를 유효성 검사에서 제외시키고 데이터 조회시에는 출력&#39;</strong> 하도록 함 </li>
</ul>
<h2 id="delete--put">DELETE &amp; PUT</h2>
<p>파일 경로 ➡️ articles/views.py</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/d738c3e3-e7ac-4820-95c7-23d0cc5250d4/image.png" alt=""></p>
<h1 id="n1-역참조-데이터-조회">[N:1 역참조 데이터 조회]</h1>
<ol>
<li>특정 게시글에 작성된 댓글 목록 출력하기</li>
</ol>
<ul>
<li>기존 필드 override</li>
</ul>
<ol start="2">
<li>특정 게시글에 작성된 댓글의 개수 출력하기</li>
</ol>
<ul>
<li>새로운 필드 추가</li>
</ul>
<h2 id="특정-게시글에-작성된-댓글-목록-출력하기">[특정 게시글에 작성된 댓글 목록 출력하기]</h2>
<ul>
<li>기존 필드 override - Article Detail<ul>
<li>게시글 조회 시 해당 게시글의 댓글 목록까지 함께 출력하기</li>
<li>Serializer는 기존 필드를 override 하거나 추가적인 필드를 구성할 수 있음</li>
</ul>
</li>
</ul>
<h3 id="1-primarykeyrelatedfields">1. PrimaryKeyRelatedFields</h3>
<p>파일 경로 ➡️ articles/serializers.py</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/ba3123c8-94cc-49d6-a0d9-79f8d1f0cf43/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/73e82535-13e2-410e-9382-9ffe6e3a3f57/image.png" alt=""></p>
<ul>
<li>POSTMAN 댓글있는 게시글 조회 결과</li>
</ul>
<ul>
<li>models.py에서 related_name을 이용하여 이름 변경 가능 </li>
<li>역참조 시 생성되는 comment_set을 override 할 수 있음</li>
</ul>
<h3 id="2-nested-relationships">2. Nested relationships</h3>
<ul>
<li>모델 관계 상으로 참조 된 대상은 참조하는 대상의 표현에 포함되거나 중첩될 수 있음</li>
<li>이러한 중첩된 관계는 serializers를 필드로 사용하여 표현할 수 있음 </li>
<li>두 클래스의 상/하 위치를 변경해야함</li>
</ul>
<p>파일 경로 ➡️ articles/serializers.py</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/4e4703f1-7053-408a-a38b-526f504febbd/image.png" alt=""></p>
<h2 id="특정-게시글에-작성된-댓글의-개수-출력하기">[특정 게시글에 작성된 댓글의 개수 출력하기]</h2>
<ul>
<li>새로운 필드 추가 - Article Detail<ul>
<li>게시글 조회 시 해당 게시글 댓글 개수까지 함게 출력하기</li>
</ul>
</li>
</ul>
<p>파일 경로 ➡️ articles/serializers.py</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/0639d708-9073-44a8-974d-5ea5f0387c39/image.png" alt=""></p>
<ul>
<li>line 3 : source</li>
<li>serializes field&#39;s argument</li>
<li>필드를 채우는 데 사용할 속성의 이름 </li>
<li>점 표기법을 사용하여 속성을 탐색할 수 있음</li>
</ul>
<h1 id="django-shortcuts-function">[Django shortcuts function]</h1>
<ul>
<li>django.shortcut 패키지는 개발에 도움 될 수 있는 여러 함수와 클래스를 제공</li>
<li>제공되는 shortcuts 목록<ul>
<li>render(), redirect(), <strong>get_object_or_404()</strong>, <strong>get_list_or_404()</strong></li>
</ul>
</li>
</ul>
<h3 id="1-get_object_or_404">1. get_object_or_404()</h3>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/87c798cf-60c0-4412-88dc-b1ec59a106ed/image.png" alt=""></p>
<ul>
<li>모델 manager objects에서 get()을 호출하지만, 해당 객체가 없을 땐 기존 DoesNotExist 예외 대신 Http404를 raise함</li>
</ul>
<h3 id="2-get_list_or_404">2. get_list_or_404()</h3>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/775d1c36-c7bd-4a16-92e2-3eabc14e0433/image.png" alt=""></p>
<ul>
<li>모델 manager objects에서 filter() 결과를 반환하고 해당 객체 목록이 없을 땐 Http404를 raise함 </li>
</ul>
<h3 id="왜-사용해야-할까">왜 사용해야 할까?</h3>
<ul>
<li>클라이언트 입장에서 &quot;서버에 오류가 발생하여 요청을 수행할 수 없다(500)&quot;라는 원인이 정확하지 않은 에러를 마주하기 보다는, 서버가 적절한 예외처리를 하고 클라이언트에게 올바른 에러를 전달하는 것 또한 중요한 요소이기 때문</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Django REST framework(DRF) - Article]]></title>
            <link>https://velog.io/@yoon-dh/Django-REST-frameworkDRF</link>
            <guid>https://velog.io/@yoon-dh/Django-REST-frameworkDRF</guid>
            <pubDate>Mon, 17 Apr 2023 08:31:48 GMT</pubDate>
            <description><![CDATA[<h2 id="drf란">DRF란?</h2>
<ul>
<li><p>Django REST framework 는 django에서 Restful API 서버를 쉽게 구축할 수 있도록 도와주는 오픈소스 라이브러리</p>
</li>
<li><p>Web API 구축을 위한 강력한 toolkit을 제공</p>
</li>
<li><p>DRF의 serializer는 django의 Form 및 ModelForm 클래스와 매우 유사하게 작동</p>
</li>
</ul>
<h3 id="실습-사전-준비">[실습 사전 준비]</h3>
<ul>
<li>가상환경 생성 및 실행</li>
<li>pip install<pre><code>$ pip install django==3.2.18 djangorestframework django-seed psycopg2</code></pre></li>
<li>프로젝트 생성, articles 앱 생성, urls 등록</li>
</ul>
<p>파일 경로 ==&gt; settings.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/883f9b90-4851-435c-b657-b47c42e561c6/image.png" alt=""></p>
<p>파일 경로 ==&gt; myapi/urls.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/b35d22bc-2858-45be-a6d6-ae91a06bc621/image.png" alt=""></p>
<p>파일 경로 ==&gt; articles/urls.py</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/f4dcc0ea-bf06-47af-a481-776a7651b577/image.png" alt=""></p>
<ul>
<li>view 임시 작성 </li>
</ul>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/5c7eb8be-d3d6-435b-9c28-283afc9104ea/image.png" alt=""></p>
<ul>
<li>Articles 모델 작성 및 migration</li>
</ul>
<p>파일 경로 ==&gt; articles/models.py</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/fd27f991-52fc-4f35-a6b7-ba85fdacf45f/image.png" alt=""></p>
<pre><code>$ python manage.py makemigrations

$ python manage.py migrate</code></pre><ol start="5">
<li>django-seed 이용해 임의 데이터 생성</li>
</ol>
<pre><code>$ python manage.py seed articles --number 20</code></pre><h3 id="modelserializer">[ModelSerializer]</h3>
<ul>
<li>serializers.py 생성</li>
<li>ModelSerializer 작성 </li>
</ul>
<p>파일 경로 ==&gt; articles/serializers.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/3b0a0019-f171-484d-a877-b4978969ae15/image.png" alt=""></p>
<p><strong>ModelSerializer</strong></p>
<ul>
<li>ModelSerializer 클래스는 모델 필드에 해당하는 필드가 있는 Serializer 클래스를 자동으로 만들 수 있는 shortcut을 제공</li>
</ul>
<ol>
<li>Model 정보에 맞춰 자동으로 필드를 생성</li>
<li>serializer에 대한 유효성 검사기를 자동으로 생성</li>
<li>.create() 및 .update()의 간단한 기본 구현이 포함됨</li>
<li>단일 객체 인스턴스 대신 QuerySet 또는 객체 목록을 serialize하려면 many=True를 작성해야함(ex. serializer = ArticleListSerializer(articles,many=True) )</li>
</ol>
<h2 id="build-restful-api---article">[Build RESTful API - Article]</h2>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/603df56b-6e46-4947-bf4f-b386fa3988ec/image.png" alt=""></p>
<h3 id="get---list">GET - LIST</h3>
<ul>
<li>게시글 데이터 목록 조회하기</li>
<li>DRF에서 api_view 데코레이터 작성은 필수</li>
</ul>
<p>파일 경로 ==&gt; articles/views.py</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/dfb219e5-34df-4a47-bc81-d9d5a2ab7497/image.png" alt=""></p>
<h3 id="get---detail">GET - DETAIL</h3>
<ul>
<li>단일 게시글 데이터 조회하기</li>
<li>각 데이터의 상세정보를 제공하는 ArticleSerializer 정의</li>
</ul>
<p>파일 경로 ==&gt; articles/serializers.py</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/eb5c4961-92b5-456e-becd-b00422f4dd6b/image.png" alt=""></p>
<p>파일 경로 ==&gt; articles/urls.py</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/c8f5be05-33fa-4eb9-903e-cf3b143083d5/image.png" alt=""></p>
<p>파일 경로 ==&gt; articles/views.py</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/57c5e0f6-0b03-4638-9345-a30194d8f7f7/image.png" alt=""></p>
<h3 id="post">POST</h3>
<ul>
<li>게시글 데이터 생성하기</li>
<li>요청에 대한 데이터 생성이 성공했을 경우는 201 Created 상태 코드를 응답하고 실패 했을 경우는 400 Bad request를 응답</li>
</ul>
<p>파일 경로 ==&gt; articles/views.py</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/0fa67ad1-6caa-4812-a082-4c5765c9b81e/image.png" alt=""></p>
<ul>
<li>POSTMAN 결과 예시 </li>
</ul>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/c771b4e7-37fb-43e0-8561-293ba2805860/image.png" alt=""></p>
<p><strong>Raising an exception on invalid data</strong></p>
<ul>
<li>유효하지 않은 데이터에 대해 예외 발생시키기</li>
<li>is_valid()는 유효성 검사 오류가 있는 경우 ValidationError 예외를 발생시키는 선택적 raise-exception 인자를 사용할 수 있음</li>
<li>DRF에서 제공하는 기본 예외 처리기에 의해 자동으로 처리되며 기본적으로 HTTP 400 응답을 반환 </li>
</ul>
<p>파일 경로 ==&gt; articles/views.py</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/2992bc1a-9965-4843-97cf-cba99dcee499/image.png" alt=""></p>
<h3 id="delete">DELETE</h3>
<ul>
<li>게시글 데이터 삭제하기</li>
<li>요청에 대한 데이터 삭제가 성공하였을 경우는 204 No Content 상태 코드 응답(명령을 수행했고 더 이상 제공할 정보가 없는 경우)</li>
</ul>
<p>파일 경로 ==&gt; articles/views.py</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/68b8079a-3671-415c-b458-f22720480443/image.png" alt=""></p>
<h3 id="put">PUT</h3>
<ul>
<li>게시글 데이터 수정하기</li>
<li>요청에 대한 데이터 수정이 성공했을 경우는 200 OK 상태 코드 응답</li>
</ul>
<p>파일 경로 ==&gt; articles/views.py</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/c60588d7-a19b-4e31-b287-741e67ffbebe/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[DB (M:N / User - User 'Follow'기능)]]></title>
            <link>https://velog.io/@yoon-dh/DB-MN-User-User-Follow%EA%B8%B0%EB%8A%A5</link>
            <guid>https://velog.io/@yoon-dh/DB-MN-User-User-Follow%EA%B8%B0%EB%8A%A5</guid>
            <pubDate>Thu, 13 Apr 2023 06:50:41 GMT</pubDate>
            <description><![CDATA[<p>User 자기 자신과의 M:N 관계 설정을 통한 팔로우 기능 구현하기</p>
<h2 id="profile">[Profile]</h2>
<ul>
<li>자연스러운 follow 흐름을 위해 Profile page 먼저 생성</li>
</ul>
<p>파일 경로 ➡️ accounts/urls.py</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/fd984f66-7dbd-4036-959a-040f14bbd59e/image.png" alt=""></p>
<p>파일 경로 ➡️ accounts/views.py</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/cbea874c-6976-44d4-9b82-eeb32bde048f/image.png" alt=""></p>
<p>파일 경로 ➡️ accounts/profile.html</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/d506de2e-eff8-4a8d-a881-b0bb37b30b18/image.png" alt=""></p>
<p>파일 경로 ➡️ templates/base.html</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/5a312824-8844-4ee3-bac9-dddff97f0143/image.png" alt=""></p>
<p>파일 경로 ➡️ articles/index.html</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/0b9108af-4bba-4d45-b6f5-1247ad70ca12/image.png" alt=""></p>
<h2 id="follow">[Follow]</h2>
<h3 id="모델-관계-설정">[모델 관계 설정]</h3>
<p>파일 경로 ➡️ accounts/models.py</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/8bb16951-594e-4168-83ff-94afacbee5f5/image.png" alt=""></p>
<pre><code>$ python manage.py makemigrations

$ python manage.py migrate</code></pre><h3 id="follow-구현">[Follow 구현]</h3>
<p>파일 경로 ➡️ accounts/urls.py</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/68f286a9-8301-4774-b808-737ff16e178d/image.png" alt=""></p>
<p>파일 경로 ➡️ accounts/views.py</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/4cbe2fd6-bc76-42ad-b1dd-0f692ec83610/image.png" alt=""></p>
<p>파일 경로 ➡️ accounts/profile.html</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/618cc95c-338c-4b00-bf98-9b1e92f1fde1/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[DB (M:N / Article - User 'Like'기능)]]></title>
            <link>https://velog.io/@yoon-dh/DB-MN-Article-User-Like%EA%B8%B0%EB%8A%A5</link>
            <guid>https://velog.io/@yoon-dh/DB-MN-Article-User-Like%EA%B8%B0%EB%8A%A5</guid>
            <pubDate>Wed, 12 Apr 2023 14:27:40 GMT</pubDate>
            <description><![CDATA[<h2 id="manytomanyfield">ManyToManyField</h2>
<ul>
<li>다대다(M:N) 관계 설정 시 사용하는 모델 필드</li>
<li>하나의 필수 위치인자(M:N관계로 설정할 모델 클래스)가 필요 </li>
<li>모델 필드의 Related Manager를 사용하여 관련 개체를 추가, 제거 또는 만들 수 있음<ul>
<li>ex. add(), remove(), create(), clear() etc.</li>
</ul>
</li>
</ul>
<ul>
<li>django는 다대다 관계를 나타내는 중개 테이블을 만듦</li>
<li>테이블 이름은 ManyToManyField 이름과 이를 포함하는 모델의 테이블 이름을 조합하여 생성됨</li>
<li>&#39;db_table&#39; arguments를 사용하여 중개 테이블의 이름을 변경할 수도 있음 </li>
</ul>
<h3 id="manytomanyfields-arguments">[ManyToManyField&#39;s Arguments]</h3>
<p>*<em>1. related_name *</em></p>
<ul>
<li>target model이 source model을 참조할 때 사용할 manager name</li>
<li>ForeignKey의 related_name과 동일</li>
</ul>
<p><strong>2. through</strong></p>
<ul>
<li>중개 테이블을 직접 작성하는 경우, through 옵션을 사용하여 중개 테이블을 나타내는 django 모델을 지정 </li>
<li>일반적으로 중개 테이블에 추가 데이터를 사용하는 다대다 관계와 연결하려는 경우에 사용됨</li>
</ul>
<p><strong>3. symmetrical</strong></p>
<ul>
<li>default : True</li>
<li>ManyToManyField가 동일한 모델(on self)를 가리키는 정의에서만 사용 </li>
<li>True인 경우 <ul>
<li>_set매니저를 추가하지 않음 </li>
<li>source 모델의 인스턴스가 target 모델의 인스턴스를 참조하면 자동으로 target 모델 인스턴스도 source 모델 인스턴스를 자동으로 참조하도록 함(대칭)</li>
</ul>
</li>
<li>대칭을 원하지 않는 경우 False로 설정</li>
</ul>
<h3 id="related-manager">[Related Manager]</h3>
<ul>
<li><p>같은 이름의 메서드여도 각 관계(N:1, M:N)에 따라 다르게 사용 및 동작됨</p>
<ul>
<li>N:1에서는 target 모델 객체만 사용 가능</li>
<li>M:N 관계에서는 관련된 객체에서 모두 사용 가능 </li>
</ul>
</li>
<li><p>메소드 종류</p>
<ul>
<li>add(), remove(), create(), clear(), set() 등</li>
</ul>
</li>
</ul>
<p><em>add()</em></p>
<ul>
<li>&quot;지정된 객체를 관련 객체 집합에 추가&quot;</li>
<li>이미 존재하는 관계에 사용하면 관계가 복제되지 않음</li>
<li>모델 인스턴스, 필드 값(PK)을 인자로 허용</li>
</ul>
<p><em>remove()</em></p>
<ul>
<li>&quot;관련 객체 집합에서 지정된 모델 개체를 제거&quot;</li>
<li>내부적으로 QuerySet.delete()를 사용하여 관계가 삭제됨</li>
<li>모델 인스턴스, 필드 값(PK)을 인자로 허용 </li>
</ul>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/f10d3d7e-3a89-4eee-b1a5-f147807a442e/image.png" alt=""></p>
<h2 id="like좋아요-기능">LIKE(좋아요 기능)</h2>
<ul>
<li>Article과 User의 M:N 관계 설정을 통한 좋아요 기능 구현하기</li>
</ul>
<h3 id="모델-관계-설정">[모델 관계 설정]</h3>
<p>파일 경로 ➡️ artiles/models.py</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/cacdac59-118d-4c6b-a23e-b641a48323fb/image.png" alt=""></p>
<ul>
<li>ManyToManyField 작성</li>
</ul>
<p>모델 변경 후 migration 작업 진행</p>
<pre><code>$ python manage.py makemigrations</code></pre><p><img src="https://velog.velcdn.com/images/yoon-dh/post/4ed68d84-c4d6-45ec-8dc0-b2dc823467dd/image.png" alt=""></p>
<p>➡️ like_user 필드 생성 시 자동으로 역참조에는 .article_set 매니저가 생성됨</p>
<ul>
<li>그러나 이전 N:1(Article-User)관계에서 이미 해당 매니저를 사용 중</li>
<li>user.article_set.all() : 해당 유저가 작성한 모든 게시글 조회</li>
<li><strong>user가 작성한 글들(user.article_set)</strong>과 <strong>user가 좋아요를 누른 글(user.article_set)</strong>을 구분할 수 없게 됨</li>
</ul>
<ul>
<li>user와 관계된 ForeignKey 혹은 ManyToManyField 중 하나에 related_name 을 작성해야함 </li>
</ul>
<p>파일 경로 ➡️ artiles/models.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/5e73f41b-38e4-48d1-b30f-0dfc992d8310/image.png" alt=""></p>
<pre><code>$ python manage.py makemigrations

$ python manage.py migrate</code></pre><ul>
<li>ManyToManyField에 related_name 을 작성후 migration </li>
</ul>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/92f78164-344e-4a92-94a4-16edcfad6f21/image.png" alt=""></p>
<h3 id="like-구현">LIKE 구현</h3>
<p>파일 경로 ➡️ articles/urls.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/5027e39c-81fe-4f74-90e2-55550d8b331d/image.png" alt=""></p>
<p>파일 경로 ➡️ articles/views.py</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/1579c1cb-f64b-4382-9720-bd4f1d8db3c1/image.png" alt=""></p>
<p>파일 경로 ➡️ articles/index.html</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/446976a2-6cf6-4ecf-b754-ffa3560c16b1/image.png" alt=""></p>
<ul>
<li>line 16-25 추가 : 좋아요 버튼 출력</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[DB (N:1 / Article - User & Comment - User)]]></title>
            <link>https://velog.io/@yoon-dh/DB-N1-Article-USER</link>
            <guid>https://velog.io/@yoon-dh/DB-N1-Article-USER</guid>
            <pubDate>Wed, 12 Apr 2023 06:57:40 GMT</pubDate>
            <description><![CDATA[<ul>
<li>Article(N) : User(1)</li>
<li>Article 모델과 User 모델 간 관계 설정 </li>
<li>N개 이상의 게시글은 1개의 회원에 의해 작성될 수 있음</li>
</ul>
<h3 id="referencing-the-user-model">Referencing the User model</h3>
<h4 id="1-settingauth_user_model">1. setting.AUTH_USER_MODEL</h4>
<ul>
<li>반환 값 : &#39;accounts.User&#39;(문자열)</li>
<li>User 모델에 대한 외래 키 또는 M:N관계를 정의할 때 사용 </li>
<li>*<em>models.py의 모델 필드에서 User 모델을 참조할 때 사용 *</em></li>
</ul>
<h4 id="2-get_user_model">2. get_user_model()</h4>
<ul>
<li>반환 값 : User Object (객체)</li>
<li>현재 활성화된 User모델을 반환</li>
<li>커스터마이징한 User모델이 있을 경우는 CustomUser모델, 그렇지 않으면 User를 반환</li>
<li>*<em>models.py가 아닌 다른 모든 곳에서 유저모델을 참조할 때 사용 *</em></li>
</ul>
<h2 id="article---user-crud">Article - User CRUD</h2>
<h3 id="모델-관계-설정">[모델 관계 설정]</h3>
<p><strong>월요일 교재 82p 사진 첨부</strong></p>
<p>파일 경로 ==&gt; articles/models.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/16758d49-5488-4fb0-8eff-655a7f0d8a6e/image.png" alt=""></p>
<pre><code>$ python manage.py makemigrations</code></pre><p>기존에 존재하던 테이블에 새로운 컬럼이 추가되어야하는 상황이기때문에 migration 파일이 곧바로 만들어지지않고 일련의 과정이 필요 </p>
<p><strong>월요일 교재 85,86p 사진 첨부</strong></p>
<pre><code>$ python manage.py migrate</code></pre><h3 id="create">CREATE</h3>
<ul>
<li>인증된 회원의 게시글 작성 구현하기</li>
<li>작성하기 전 로그인을 먼저 한 상태로 진행 </li>
</ul>
<p>파일 경로 ==&gt; articles/forms.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/b5c1ed9e-d5e8-4776-b5e9-3368b444217c/image.png" alt=""></p>
<ul>
<li>ArticleForm 출력시 불필요한 user필드도 함께 출력됨</li>
<li>line6 : exclude = (&#39;user&#39;,) 로도 대체 가능</li>
</ul>
<p>파일 경로 ==&gt; articles/views.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/daa2007c-805c-41dd-b9a2-d5348135114a/image.png" alt=""></p>
<ul>
<li>이전 상태의 create함수를 이용해 게시글을 작성하면 외래 키에 저장되어야할 작성자 정보가 누락되기 때문에 create 함수 수정 필요</li>
</ul>
<h3 id="read">READ</h3>
<ul>
<li>index.html &amp; detail.html에서 각 게시글의 작성자 출력</li>
</ul>
<p>파일 경로 ==&gt; articles/index.html
<img src="https://velog.velcdn.com/images/yoon-dh/post/ee66cd8d-cd51-402c-b3c7-f878ab5b3bc2/image.png" alt=""></p>
<p>파일 경로 ==&gt; articles/detail.html
<img src="https://velog.velcdn.com/images/yoon-dh/post/425a64cc-8213-4187-80c9-5ec543174988/image.png" alt=""></p>
<h3 id="update">UPDATE</h3>
<ul>
<li>수정을 요청하는 사람과 게시글을 작성한 사람을 비교하여 본인의 게시글만 수정할 수 있도록 함</li>
</ul>
<p>파일 경로 ==&gt; articles/views.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/3a71a56e-328f-465f-958d-b33b42742a7c/image.png" alt=""></p>
<p>파일 경로 ==&gt; articles/detail.html
<img src="https://velog.velcdn.com/images/yoon-dh/post/e53b6dc9-8759-4084-8918-f640286a2089/image.png" alt=""></p>
<ul>
<li>해당 게시글의 작성자가 아니라면 수정/삭제 버튼 자체를 출력해주지 않음</li>
</ul>
<h3 id="delete">DELETE</h3>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/75dda586-b6ba-40b4-bb39-9fe3bb7a4af8/image.png" alt=""></p>
<h2 id="comment---user-crud">Comment - User CRUD</h2>
<ul>
<li>Comment(N) - User(1)</li>
</ul>
<h3 id="모델-관계-설정-1">[모델 관계 설정]</h3>
<p><strong>화요일 교재 6p 사진 첨부</strong></p>
<p>파일 경로 ==&gt; articles/models.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/a05e9620-14b0-4a6c-91df-dbe1be7588d5/image.png" alt=""></p>
<ul>
<li>Comment 모델에 User모델을 참조하는 외래키 작성</li>
</ul>
<pre><code>$ python manage.py makemigrations

      -위의 Article_User와 같은 과정 필요

$ python manage.py migrate </code></pre><h3 id="create-1">CREATE</h3>
<ul>
<li>인증된 회원의 댓글 작성 구현</li>
<li>작성하기 전 로그인 미리 한 상태에서 진행</li>
</ul>
<p>파일 경로 ==&gt; articles/forms.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/3b38720b-1804-4fc9-bf85-3ecdacb7f544/image.png" alt=""></p>
<ul>
<li>CommentForm 출력 시 불필요한 피드 user가 출력됨</li>
<li>exclude 사용해 제외</li>
</ul>
<p>파일 경로 ==&gt; articles/views.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/d8320a11-e322-48e7-a758-8fe0a0259bf5/image.png" alt=""></p>
<ul>
<li>댓글 작성 시 외래 키에 저장되어야 할 작성자 정보가 누락 ==&gt; 막아주기 위해 comments_create 함수 수정, comment.user에 현재 요청한 request.user를 넣어줌 </li>
</ul>
<h3 id="read-1">READ</h3>
<p>파일 경로 ==&gt; articles/detail.html</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/c1e316e4-7f6f-441b-9372-007489672b0b/image.png" alt=""></p>
<ul>
<li>댓글과 댓글 작성자가 함께 보이게 수정</li>
</ul>
<h3 id="delete-1">DELETE</h3>
<p>파일 경로 ==&gt; articles/views.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/3595e8b8-22b8-451c-b76c-0602a0c08ca7/image.png" alt=""></p>
<ul>
<li>삭제 요청한 사람과 댓글 작성자가 같을때만 삭제가 가능하도록 함</li>
</ul>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/0db66bf7-8ed8-4da2-b4ff-cef1ed86b9ea/image.png" alt=""></p>
<ul>
<li>추가로 해당 댓글의 작성자가 아니라면, 삭제 버튼 자체를 출력하지 않음</li>
</ul>
<h2 id="인증된-사용자에-대한-접근-제한">인증된 사용자에 대한 접근 제한</h2>
<ul>
<li>is_authenticated와 View decorator 활용</li>
</ul>
<p>파일 경로 ==&gt; articles/views.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/d59c163b-2762-49c0-9143-69556a5612b5/image.png" alt=""></p>
<ul>
<li>from django.views.decorators.http import require_POST : 최상단 import</li>
</ul>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/2ddf1814-60a6-4a47-a27d-b4b5433f7a93/image.png" alt=""></p>
<p>파일 경로 ==&gt; articles/detail.html
<img src="https://velog.velcdn.com/images/yoon-dh/post/7b2c04bf-0943-4d22-be00-43233db052ad/image.png" alt=""></p>
<ul>
<li>로그인되지않은 사용자에게는 CommentForm 자체를 보여주지 않음</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[DB (N:1 / Comment - Article)]]></title>
            <link>https://velog.io/@yoon-dh/DB-N1-Comment-Article</link>
            <guid>https://velog.io/@yoon-dh/DB-N1-Comment-Article</guid>
            <pubDate>Wed, 12 Apr 2023 06:21:55 GMT</pubDate>
            <description><![CDATA[<h2 id="foreign-key">Foreign Key</h2>
<ul>
<li><p>외래 키(외부 키)</p>
</li>
<li><p>관계형 DB에서 다른 테이블의 행을 식별할 수 있는 키</p>
</li>
<li><p>참조되는 테이블의 기본 키를 가리킴(외래키의 값이 반드시 부모 테이블의 기본 키일 필요는 없지만 유일한 값이어야 함 - <strong>참조 무결성</strong>)</p>
<ul>
<li><em>참조무결성? DB 관계모델에서 관련된 2개의 테이블 간의 일관성을 말함</em></li>
<li><em>외래키가 선언된 테이블의 외래키 속성의 값은 그 테이블의 부모가 되는 테이블의 기본 키 값으로 존재해야 함</em></li>
</ul>
</li>
<li><p>참조하는 테이블의 행 1개의 값은, 참조되는 측 테이블의 행 값에 대응됨</p>
<ul>
<li>이 때문에 참조하는 테이블의 행에는, 참조되는 테이블에 나타나지 않는 값을 포함할 수 없음</li>
</ul>
</li>
<li><p>참조하는 테이블 행 여러 개가 참조되는 테이블의 동일한 행을 참조할 수 있음</p>
</li>
</ul>
<h2 id="n1-commentn--article1">N:1 (Comment(N) : Article(1))</h2>
<ul>
<li>Comment 모델과 Article 모델 간 관계 설정</li>
<li>n개 이상의 댓글은 1개의 게시글에 작성될 수 있음</li>
</ul>
<p><strong>월요일 교재 22p 사진 삽입</strong></p>
<ul>
<li>Django Relationship fields</li>
</ul>
<ol>
<li>OneToOneField() : A one to one relationship</li>
<li><strong>ForeignKey() : A many to one relationship</strong></li>
</ol>
<ul>
<li><p>N:1을 담당하는 django의 모델 피드 클래스</p>
</li>
<li><p>외래키 속성을 담당</p>
</li>
<li><p>2개의 필수 위치 인자 필요</p>
<ul>
<li>참조하는 model class</li>
<li>on delete 옵션</li>
</ul>
</li>
</ul>
<ol start="3">
<li>ManyToManyField() : A many to many relationship</li>
</ol>
<h3 id="comment-모델-정의">Comment 모델 정의</h3>
<p>파일 경로 ==&gt; articles/models.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/afafaa47-de7e-4d44-888b-2f1edfef4f56/image.png" alt=""></p>
<ul>
<li>외래 키 필드는 ForeignKey 클래스를 작성하는 위치와 관계없이 필드의 마지막에 작성됨</li>
<li>ForeignKey() 클래스의 인스턴스 이름은 참조하는 모델 클래스 이름의 단수형(소문자)로 작성하는 것을 권장<ul>
<li>ForeignKey 인스턴스를 생성하면 <strong>인스턴스이름_id</strong>로 컬럼이 생성된다.(명시적인 모델관계 파악을 위해)</li>
</ul>
</li>
<li>on_delete : 외래키가 참조하는 객체가 사라졌을 때, 외래키를 가진 객체를 어떻게 처리할 지를 정의(ex.CASCADE : 부모객체가 삭제됐을 때, 이를 참조하는 객체도 삭제)</li>
</ul>
<pre><code>$ python manage.py makemigrations

$ python manage.py migrate</code></pre><ul>
<li>모델에 대한 수정사항이 발생했기 때문에 migration 과정 진행</li>
</ul>
<p><strong>admin site 등록</strong>
파일 경로 ==&gt; articles/admin.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/d79f15a6-273d-4441-b7f2-eb107ff7009d/image.png" alt=""></p>
<h4 id="관계-모델-참조">[관계 모델 참조]</h4>
<ul>
<li><p>Related manager</p>
<ul>
<li>Related manager는 N:1 혹은 M:N 관계에서 사용가능한 문맥</li>
<li>django는 모델 간 N:1 혹은 M:N 관계가 설정되면 역참조할 때에 사용할 수 있는 manager를 생성</li>
</ul>
</li>
</ul>
<h4 id="역참조">[역참조]</h4>
<ul>
<li>나를 참조하는 테이블(나를 외래키로 지정한)을 참조하는 것</li>
<li>즉, 본인을 외래키로 참조 중인 테이블에 접근하는 것</li>
<li>N:1 관계에서는 1이 N을 참조하는 상황(외래키를 가지지 않은 Article이 외래키를 가진 Comment를 참조)</li>
</ul>
<pre><code>article.comment_set.method()</code></pre><ul>
<li>Article 모델이 Comment모델을 참조(역참조)할 때 사용하는 매니저</li>
<li>django가 역참조 할 수 있는 comment_set 매니저를 자동 생성해 article.comment_set 형태로 댓글 객체를 참조할 수 있음</li>
<li>N:1 관계에서 생성되는 Related Manager의 이름은 참조하는 &quot;<strong>모델명_set</strong>&quot; 이름 규칙으로 만들어짐</li>
<li>반면 참조 상황(Comment ==&gt; Article)에서는 실제 ForeignKey클래스로 작성한 인스턴스가 Comment 클래스의 클래스 변수이기 때문에 comment.article 형태로 작성 가능</li>
</ul>
<h2 id="comment-crud">Comment CRUD</h2>
<h3 id="create">CREATE</h3>
<p>파일 경로 ==&gt; articles/forms.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/eda5c634-5ba0-49e0-93d7-67719aea0b24/image.png" alt=""></p>
<ul>
<li>사용자로부터 댓글 데이터를 입력 받기 위한 CommentForm 작성</li>
<li>exclude이용하여 article 제외한 나머지를 보여줌</li>
</ul>
<p>파일 경로 ==&gt; articles/views.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/ed1e2553-9a14-4892-aab1-4a071e578c6a/image.png" alt=""></p>
<ul>
<li>최상단 CommentForm import 할 것 </li>
</ul>
<p>파일 경로 ==&gt; articles/urls.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/d0ec4e44-09db-4100-bfa3-6f1a00ff25fa/image.png" alt=""></p>
<ul>
<li>댓글의 외래 키 데이터에 필요한 정보가 게시글의 pk값이기 때문에 url을 통해 변수를 넘기는 variable routing을 사용</li>
</ul>
<p>파일 경로 ==&gt; articles/views.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/090732ec-8e64-4b44-aefd-e61856c18a91/image.png" alt=""></p>
<ul>
<li><p>save(commit=False)</p>
<ul>
<li>아직 데이터베이스에 저장되지 않은 인스턴스를 반환</li>
<li>저장하기 전에 객체에 대한 사용자 지정 처리를 수행할 때 유용하게 사용 </li>
</ul>
</li>
</ul>
<p>파일 경로 ==&gt; articles/detail.html
<img src="https://velog.velcdn.com/images/yoon-dh/post/827a9283-a786-402c-afa1-f38b4788a93b/image.png" alt=""></p>
<ul>
<li>line 28 - 32 : commentForm 추가</li>
</ul>
<h3 id="read">READ</h3>
<p>파일 경로 ==&gt; articles/views.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/5016b6a4-26be-4acb-ab2c-197fabf9db0c/image.png" alt=""></p>
<p>파일 경로 ==&gt; articles/detail.html
<img src="https://velog.velcdn.com/images/yoon-dh/post/0c5b6f5f-7e15-4e60-8445-7b483d7abc5a/image.png" alt=""></p>
<ul>
<li>line 28 - 34 추가</li>
</ul>
<h3 id="update">UPDATE</h3>
<ul>
<li>댓글 수정도 게시글 수정과 마찬가지로 구현이 가능하지만 댓글 수정 페이지가 따로 필요하게 됨</li>
<li>하지만 일반적으로 댓글 수정은 수정 페이지로 이동 없이 현재 페이지를 유지한 상태로 댓글 작성 Form 부분만 변경되어 수정할 수 있도록 함</li>
<li>이처럼 페이지의 일부 내용만 업데이트하는 것은 JS의 영역(현재 진행X)</li>
</ul>
<h3 id="delete">DELETE</h3>
<p>파일 경로 ==&gt; articles/urls.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/b5fa1e2d-643c-4adb-877e-c1569fd6a6e9/image.png" alt=""></p>
<p>파일 경로 ==&gt; articles/views.py</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/f653e290-fef3-490f-8566-0f3cfa8230b3/image.png" alt=""></p>
<ul>
<li>최상단 Comment import</li>
</ul>
<p>파일 경로 ==&gt; articles/detail.html
<img src="https://velog.velcdn.com/images/yoon-dh/post/8cf56fdc-56d9-4587-911a-7a7460549988/image.png" alt=""></p>
<ul>
<li>line 33 - 35 추가</li>
</ul>
<h2 id="comment-추가사항">Comment 추가사항</h2>
<h3 id="1-댓글-개수-출력하기">1. 댓글 개수 출력하기</h3>
<p>1-1. DTL filter - <strong>length</strong> 사용 </p>
<pre><code>{{ comments|length }}

{{ article.comment_set.all|length }}</code></pre><p>1-2. Queryset API - <strong>count()</strong> 사용 </p>
<pre><code>{{ comment.count }}

{{ article.comment_set.count }}</code></pre><p>파일 경로 ==&gt; articles/detail.html
<img src="https://velog.velcdn.com/images/yoon-dh/post/ea585230-3035-4525-95dd-2faddac239ce/image.png" alt=""></p>
<ul>
<li>line 29 - 31 추가</li>
</ul>
<h3 id="2-댓글-없는-경우-대체-컨텐츠">2. 댓글 없는 경우 대체 컨텐츠</h3>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/a94fe0a9-7778-4555-a2ce-4ab453fde331/image.png" alt=""></p>
<ul>
<li><strong>for empty</strong> 문 사용 </li>
<li>line 41-42 추가</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[SQL syntax]]></title>
            <link>https://velog.io/@yoon-dh/SQL-syntax</link>
            <guid>https://velog.io/@yoon-dh/SQL-syntax</guid>
            <pubDate>Wed, 05 Apr 2023 15:02:19 GMT</pubDate>
            <description><![CDATA[<h2 id="sql-commads">SQL commads</h2>
<ol>
<li><strong>DDL(Data Definition Language)</strong></li>
</ol>
<ul>
<li>데이터 정의 언어 : 관계형 데이터베이스를 구조(테이블, 스키마)를 정의(생성, 수정 및 삭제)하기 위한 명령어</li>
<li>ex) CREATE, DROP, ALTER</li>
</ul>
<ol start="2">
<li><strong>DML(Data Manipulation Language)</strong></li>
</ol>
<ul>
<li>데이터 조작 언어 : 데이터를 조작(추가, 조회, 변경, 삭제) 하기 위한 명령어</li>
<li>ex) INSERT, SELECT, UPDATE, DELETE</li>
</ul>
<ol start="3">
<li><strong>DCL(Data Control Language)</strong></li>
</ol>
<ul>
<li>데이터 제어 언어 : 데이터의 보안, 수행제어, 사용자 권한 부여 등을 정의하기 위한 명령어</li>
<li>ex) GRANT, REVOKE, COMMIT, ROLLBACK</li>
</ul>
<h3 id="ddl-index">[DDL INDEX]</h3>
<h4 id="create-table">[CREATE TABLE]</h4>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/5410d31e-3144-4380-b278-107d4fc7fabb/image.png" alt=""></p>
<p>데이터 타입 종류</p>
<ol>
<li>NULL</li>
</ol>
<ul>
<li>NULL value</li>
<li>정보가 없거나 알 수 없음을 의미</li>
</ul>
<ol start="2">
<li>INTEGER</li>
</ol>
<ul>
<li>정수</li>
<li>크기에따라 0,1,2,3,4,6 또는 8바이트 같은 가변 크기를 가짐</li>
</ul>
<ol start="3">
<li>REAL </li>
</ol>
<ul>
<li>실수 </li>
<li>8바이트 부동 소수점을 사용하는 10진수 값이 있는 실수 </li>
</ul>
<ol start="4">
<li>TEXT</li>
</ol>
<ul>
<li>문자 데이터 </li>
</ul>
<p>5.BLOB(Binary Large Object)</p>
<ul>
<li>입력된 그대로 저장된 데이터 덩어리(대용타입 없음)</li>
<li>바이너리 등 멀티미디어 파일</li>
<li>ex) 이미지 데이터 </li>
</ul>
<ol start="6">
<li>Boolean type</li>
</ol>
<ul>
<li>SQLite에는 별도의 Boolean 타입이 없음 </li>
<li>대신 정수 0(False)와 1(True)로 저장됨</li>
</ul>
<p><strong>타입 선호도(Type Affinity)</strong></p>
<ul>
<li>특정 컬럼에 저장된 데이터에 권장되는 타입</li>
<li>데이터 타입 작성 시 SQLite의 5가지 데이터 타입이 아닌 다른 데이터 타입을 선언한다면, 내부적으로 각 타입의 지정된 선호도에 따라 5가지 선호도로 인식됨 </li>
</ul>
<ol>
<li>INTEGER</li>
<li>TEXT</li>
<li>BLOB</li>
<li>REAL</li>
<li>NUMERIC<img src="https://velog.velcdn.com/images/yoon-dh/post/00d8d490-220f-4de9-95b0-85b612d36984/image.png" alt=""></li>
</ol>
<ul>
<li>다른 데이터베이스 엔진 간의 호환성을 최대화</li>
<li>정적이고 엄격한 타입을 사용하는 DB의 SQL문을 SQLite에서도 작동하도록 하기 위함 </li>
</ul>
<p><strong>제약조건(Constraints)</strong></p>
<ul>
<li>입력하는 자료에 대해 제약을 정함</li>
<li>제약에 맞지 않다면 입력이 거부됨</li>
<li>사용자가 원하는 조건의 데이터만 유지하기 위한 즉, 데이터의 무결성을 유지하기 위한 보편적인 방법으로 테이블의 특정 컬럼에 설정하는 제약<ul>
<li>_데이터 무결성이란? DB내의 데이터에 대한 정확성, 일관성을 보장하기 위해 데이터 변경 혹은 수정 시 여러 제한을 두어 데이터의 정확성을 보증하는 것 _</li>
</ul>
</li>
</ul>
<p><strong>Constraints 종류</strong></p>
<ol>
<li>NOT NULL </li>
</ol>
<ul>
<li>컬럼이 NULL 값을 허용하지 않도록 지정</li>
<li>기본적으로 테이블의 모든 컬럼은 NOT NULL을 명시하지 않는 경우 NULL 값을 허용</li>
</ul>
<ol start="2">
<li>UNIQUE</li>
</ol>
<ul>
<li>컬럼의 모든 값이 서로 구별되거나 고유한 값이 되도록 함</li>
</ul>
<ol start="3">
<li>PRIMARY KEY</li>
</ol>
<ul>
<li>테이블에서 행의 고유성을 식별하는 데 사용되는 컬럼</li>
<li>각 테이블에는 하나의 기본 키만 있음</li>
<li>암시적으로 NOT NULL 제약조건이 포함되어있음 </li>
</ul>
<ol start="4">
<li>AUTOINCREMENT </li>
</ol>
<ul>
<li>사용되지 않은 값이나 이전에 삭제된 행의 값을 재사용하는 것을 방지</li>
<li>INTEGER PRIMARY KEY 다음에 작성하면 해당 rowid를 다시 재사용하지 못하도록 함</li>
</ul>
<p><strong>rowid의 특징</strong></p>
<ul>
<li><p>테이블을 생성할 때마다 rowid라는 암시적 자동 증가 컬럼이 자동으로 생성됨 </p>
</li>
<li><p>테이블의 행을 고유하게 식별하는 64비트 부호있는 정수 값</p>
</li>
<li><p>테이블 새 행을 삽입할 때마다 정수 값을 자동으로 할당</p>
<ul>
<li>값은 1에서 시작 </li>
<li>데이터 삽입 시에 rowid 또는 INTEGER PRIMARY KEY 컬럼에 명시적으로 값이 지정되지 않은 경우, SQLite는 테이블에서 가장 큰 rowid보다 하나 큰 다음 순차 정수를 자동으로 할당(AUTOINCREMENT와 관계없이)</li>
</ul>
</li>
<li><p>만약 INTEGER PRIMARY KEY 키워드를 가진 컬럼을 직접 만들면 이 컬럼은 rowid 컬럼의 별칭(alias)이 됨 </p>
</li>
</ul>
<h4 id="alter-table">[ALTER TABLE]</h4>
<ul>
<li>기존 테이블의 구조를 수정</li>
</ul>
<ol>
<li><strong>RENAME</strong> a table</li>
<li><strong>RENAME</strong> a column</li>
<li><strong>ADD</strong> a new column to a table</li>
<li><strong>DELETE</strong> a column</li>
</ol>
<p><strong>RENAME a table</strong>
<img src="https://velog.velcdn.com/images/yoon-dh/post/7a1e7b0e-20d6-471c-99aa-53fa916897bf/image.png" alt=""></p>
<p><strong>RENAME a column</strong>
<img src="https://velog.velcdn.com/images/yoon-dh/post/eee8fad7-82a8-4419-9043-3b3511212974/image.png" alt=""></p>
<p><strong>ADD a new column to a table</strong>
<img src="https://velog.velcdn.com/images/yoon-dh/post/ec5da36d-07f7-463e-9186-f9f5ffa03b84/image.png" alt=""></p>
<ul>
<li>만약 테이블에 기존 데이터가 있을 경우 다음과 같은 에러가 발생함<pre><code>Cannot add NOT NULL column with default value NULL</code></pre></li>
<li>이미 이전에 저장된 데이터들은 새롬게 추가되는 컬럼에 값이 없기 때문에 NULL이 작성됨 ➡️ 그런데 새로 추가되는 컬럼에 NOT NULL 제약조건이 있기 때문에 기본 값 없이는 추가될 수 없다는 에러 </li>
<li>위의 경우 다음과 같이 DEFAULT 제약 조건을 사용하여 해결 할 수 있음
<img src="https://velog.velcdn.com/images/yoon-dh/post/d74d7a4b-014a-4399-9497-f842ceee98cb/image.png" alt=""></li>
<li>이러면 address 컬럼이 추가되면서 기존에 있던 데이터들의 address 컬럼 값은 &#39;no address&#39;가 된다.</li>
</ul>
<p><strong>DELETE a column</strong></p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/cc7b60c2-edcc-49e4-a27c-70e95501781a/image.png" alt=""></p>
<p>아래와 같은 경우 삭제하지 못함 </p>
<ul>
<li>컬럼이 다른 부분에서 참조되는 경우(FK 제약조건에서 사용되는 경우)</li>
<li>PK인 경우</li>
<li>UNIQUE 제약조건이 있는 경우 </li>
</ul>
<h4 id="drop-table">[DROP TABLE]</h4>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/eb8790c2-03a7-4bfd-96ed-148f2d8ef2c8/image.png" alt=""></p>
<ul>
<li>한 번에 하나의 테이블만 삭제 사능</li>
<li>DROP TABLE문은 실행 취소하거나 복구할 수 없음 </li>
</ul>
<h3 id="dml">[DML]</h3>
<p>*<em>csv 파일 데이터를 테이블로 넣는 작업 *</em></p>
<ol>
<li>csv 파일 속 데이터 형식과 맞게 테이블 생성
<img src="https://velog.velcdn.com/images/yoon-dh/post/6054a1eb-cab5-45a0-af94-4ee080aaadc5/image.png" alt=""></li>
<li>데이터베이스 파일 열기<pre><code>$ sqlite3 mydb.sqlite3  # mydb부분에 원하는 파일 이름</code></pre></li>
<li>모드를 csv로 설정<pre><code>$ .mode csv</code></pre></li>
<li>.import 명령어 사용하여 csv데이터를 테이블로 가져오기<pre><code>$ .import users.csv users</code></pre></li>
</ol>
<ul>
<li>.import (csv파일 명) (테이블 명)</li>
</ul>
<h4 id="simple-query">[Simple query]</h4>
<ul>
<li>SELECT문을 사용해 단일 테이블 데이터 조회</li>
<li>다양한 절과 함께 사용할 수 있다.(ORDER BY, DISTINCT, WHERE, LIMIT, LIKE, GROUP BY)</li>
</ul>
<p><em>사용 예시</em></p>
<ol>
<li>이름과 나이 조회
<img src="https://velog.velcdn.com/images/yoon-dh/post/7a1d4987-fbdc-4376-895b-654c80a3d791/image.png" alt=""></li>
<li>전체 데이터 조회
<img src="https://velog.velcdn.com/images/yoon-dh/post/265af37f-11fe-4a23-a269-46595a787f4c/image.png" alt=""></li>
</ol>
<h4 id="sorting-rows">[Sorting rows]</h4>
<ul>
<li>ORDER BY 절을 사용하여 쿼리의 결과를 정렬하기<img src="https://velog.velcdn.com/images/yoon-dh/post/46d37a2a-dab9-4938-9326-1fa660124008/image.png" alt=""></li>
</ul>
<p><em>사용 예시</em></p>
<ol>
<li>이름과 나이를 나이가 어린 순서대로 조회
<img src="https://velog.velcdn.com/images/yoon-dh/post/a3a94338-c73a-412d-9d5f-0e81fd4597b4/image.png" alt=""></li>
</ol>
<ul>
<li>ORDER BY의 default가 오름차순이라 아무것도 쓰지 않아도 괜찮다. (뒤에 ASC를 붙여도 무방함)</li>
</ul>
<ol start="2">
<li>이름과 나이를 나이 많은 순서대로 조회(내림차순)
<img src="https://velog.velcdn.com/images/yoon-dh/post/b170a8eb-8a60-4b86-a13e-f3d2f695276b/image.png" alt=""></li>
<li>이름,나이,계좌 잔고를 나이가 어린 순으로, 만약 같은 나이라면 계좌 잔고가 많은 순으로 정렬해서 조회
<img src="https://velog.velcdn.com/images/yoon-dh/post/c040e412-b9b3-4228-a0a2-b3adcbaa1e5d/image.png" alt=""></li>
</ol>
<ul>
<li>먼저 나오는 순서대로 정렬 우선순위를 가짐</li>
</ul>
<p>NULL의 정렬 방식</p>
<ul>
<li>정렬과 관련하여 SQLite는 NULL을 다른 값보다 작은 것으로 간주</li>
<li>즉, ASC를 사용하는 경우 결과의 시작 부분에 NULL이 표시되고, DESC를 사용하는 경우 결과의 끝에 NULL이 표시됨</li>
</ul>
<h4 id="filtering-data">[Filtering data]</h4>
<ul>
<li>Clause<ul>
<li>SELECT DISTINCT</li>
<li>WHERE</li>
<li>LIMIT</li>
</ul>
</li>
<li>Operator<ul>
<li>LIKE</li>
<li>IN</li>
<li>BETWEEN</li>
</ul>
</li>
</ul>
<p><strong>SELECT DISTINCT</strong></p>
<ul>
<li>조회결과에서 중복된 행을 제거</li>
<li>DISTINCT 절은 SELECT 에서 선택적으로 사용할 수 있는 절</li>
<li>DISTINCT절은 SELECT 키워드 바로 뒤에 나타나야 함 &amp; DISTINCT 키워드 뒤에 컬럼 또는 컬럼 목록을 작성 </li>
</ul>
<p><em>사용 예시</em></p>
<ol>
<li>중복 없이 모든 지역 조회
<img src="https://velog.velcdn.com/images/yoon-dh/post/57aa16db-e764-4d84-a63d-9775a25bdc06/image.png" alt=""></li>
<li>이름과 지역 중복 없이 지역순으로 오름차순 정렬하여 모든 이름과 지역 조회하기
<img src="https://velog.velcdn.com/images/yoon-dh/post/6be79159-bd8b-43eb-b1c6-e24f433b9b20/image.png" alt=""></li>
</ol>
<p><strong>WHERE</strong></p>
<ul>
<li>조회 시 특정 검색 조건을 지정</li>
<li>WHERE 절은 SELECT, UPDATE, DELETE 문에서 선택적으로 사용 할 수 있는 절</li>
<li>FROM 절 뒤에 작성<img src="https://velog.velcdn.com/images/yoon-dh/post/2aac4d0c-5ff6-4d7d-9091-4f820f61cc51/image.png" alt="">
<img src="https://velog.velcdn.com/images/yoon-dh/post/a1b42034-f418-47be-a8a9-1fa2bf371525/image.png" alt=""></li>
</ul>
<p><em>사용 예시</em></p>
<ol>
<li>나이가 30살 이상인 사람들의 이름, 나이, 계좌 잔고 조회
<img src="https://velog.velcdn.com/images/yoon-dh/post/9503eb9e-1fd0-48d3-b668-51a347afc54c/image.png" alt=""></li>
<li>나이가 30살 이상이고 계좌 잔고가 50만원 초과인 사람들의 이름,나이,계좌 잔고 조회
<img src="https://velog.velcdn.com/images/yoon-dh/post/b9ed70bb-2711-4712-ba9a-9404c75b620a/image.png" alt=""></li>
</ol>
<p><strong>LIKE</strong></p>
<ul>
<li>패턴일치를 기반으로 데이터를 조회</li>
<li>WHERE 절에서 사용</li>
<li>기본적으로 대소문자는 구분하지 않음</li>
<li>SQLite는 패턴 구성을 위한 두개의 와일드카드를 제공</li>
</ul>
<ol>
<li>% (percent)</li>
</ol>
<ul>
<li>0개 이상의 문자가 올 수 있음을 의미
<img src="https://velog.velcdn.com/images/yoon-dh/post/46ead347-a45b-46b4-b10d-be7e2bad450f/image.png" alt=""></li>
</ul>
<ol start="2">
<li>_ (underscore)</li>
</ol>
<ul>
<li>단일(1개) 문자가 있음을 의미
<img src="https://velog.velcdn.com/images/yoon-dh/post/8319589c-bbe5-4ecf-86f8-994d3d5d4a1b/image.png" alt=""></li>
</ul>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/046ea66a-0a9e-45fa-9f10-5a3bc990a2e4/image.png" alt=""></p>
<p><em>사용 예시</em></p>
<ol>
<li>이름에 &#39;호&#39;가 포함되는 사람들의 이름과 성 조회
<img src="https://velog.velcdn.com/images/yoon-dh/post/86850384-ee6e-4ad6-9959-3391c453dbae/image.png" alt=""></li>
<li>서울 지역 번호를 가진 사람들의 이름과 전화번호 조회
<img src="https://velog.velcdn.com/images/yoon-dh/post/abf2abe9-6b8b-4429-8eac-ca16a87e80ac/image.png" alt=""></li>
<li>나이가 20대인 사람들의 이름과 나이 조회하기
<img src="https://velog.velcdn.com/images/yoon-dh/post/5c64e5ab-37b1-4948-aa91-12bb91bce060/image.png" alt=""></li>
<li>전화번호 중간 4자리가 51로 시작하는 사람들의 이름과 전화번호 조회
<img src="https://velog.velcdn.com/images/yoon-dh/post/60755a18-e9c6-4ca4-8401-84bae7d4797b/image.png" alt=""></li>
</ol>
<p><strong>[IN operator]</strong></p>
<ul>
<li>값이 값 목록 결과에 있는 값과 일치하는지 확인</li>
<li>표현식이 값 목록의 값과 일치하는지 여부에 따라 T/F를 반환</li>
<li>IN 연산자의 결과를 부정하려면 NOT IN 연산자를 사용</li>
</ul>
<p><em>사용 예시</em></p>
<ol>
<li>경기도 혹은 강원도에 사는 사람들의 이름과 지역 조회하기
<img src="https://velog.velcdn.com/images/yoon-dh/post/1dc019c4-1ff0-406d-aada-d32f8ed1c943/image.png" alt=""></li>
<li>경기도 혹은 강원도에 살지 않는 사람들의 이름과 지역 조회하기
<img src="https://velog.velcdn.com/images/yoon-dh/post/5707d47c-55af-484c-a497-295ffc48de02/image.png" alt=""></li>
</ol>
<p><strong>[BETWEEN operator]</strong></p>
<ul>
<li>값이 값 범위에 있는지 테스트</li>
<li>값이 지정된 범위에 있으면 True</li>
<li>WHERE 절ㅇ레서 사용</li>
<li>BETWEEN 연산자의 결과 부정 ➡️ NOT BETWEEN 사용</li>
</ul>
<p><em>사용 예시</em></p>
<ol>
<li>나이가 20살 이상, 30살 이하인 사람들의 이름과 나이 조회
<img src="https://velog.velcdn.com/images/yoon-dh/post/834ec569-1d2d-42dc-9c82-6236c00099b0/image.png" alt=""></li>
<li>나이가 20살 이상, 30살 이하가 아닌 사람들의 이름과 나이 조회하기
<img src="https://velog.velcdn.com/images/yoon-dh/post/a6d5d8ad-4712-4f5f-bc57-b1ffc74b96bc/image.png" alt=""></li>
</ol>
<p><strong>[LIMIT clause]</strong></p>
<ul>
<li>쿼리에서 반환되는 행 수를 제한 </li>
<li>SELECT문에서 선택적으로 사용할 수 있는 절 </li>
<li>row_count는 반환되는 행 수를 지정하는 양의 정수를 의미</li>
</ul>
<p><em>사용 예시</em> </p>
<ol>
<li>첫 번쨰부터 열 번째 데이터까지 rowid와 이름 조회하기
<img src="https://velog.velcdn.com/images/yoon-dh/post/9e6b67f2-7e96-463c-8517-2c03bfb8ab07/image.png" alt=""></li>
<li>계좌잔고가 가장 많은 10명의 이름과 계좌잔고 조회
<img src="https://velog.velcdn.com/images/yoon-dh/post/b8c5e0e8-ad09-4f93-9e52-bce9ba43664e/image.png" alt=""></li>
</ol>
<p><strong>[OFFSET keyword]</strong></p>
<ul>
<li>LIMIT 절을 사용하면 첫번쨰 데이터부터 지정한 수 만큼의 데이터를 받아올 수 있지만, OFFSET과 함께 사용하면 특정 지정된 위치에서부터 데이터를 조회할 수 있음</li>
</ul>
<p><em>사용 예시</em></p>
<ol>
<li>11번째부터 20번쨰 데이터의 rowid와 이름 조회
<img src="https://velog.velcdn.com/images/yoon-dh/post/06f792aa-7f53-4d9d-8634-09dfb34ac9ef/image.png" alt=""></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[accounts CRUD]]></title>
            <link>https://velog.io/@yoon-dh/accounts-CRUD</link>
            <guid>https://velog.io/@yoon-dh/accounts-CRUD</guid>
            <pubDate>Sat, 25 Mar 2023 08:50:57 GMT</pubDate>
            <description><![CDATA[<h3 id="setting--user-modeling">setting &amp; user modeling</h3>
<p>account app 생성 </p>
<pre><code>$ python manage.py startapp accounts</code></pre><p>파일 경로 ➡️ settings.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/b2095f28-dad4-475a-ae04-47e4f95edd77/image.png" alt=""></p>
<p>파일 경로 ➡️ crud/urls.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/b6915185-c696-40fa-bccc-53a735e1b214/image.png" alt=""></p>
<p>파일 경로 ➡️ accounts/urls.py(new file)</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/15bfcfea-c2db-4a58-b950-70dc2559c5b7/image.png" alt=""></p>
<p>파일 경로 ➡️ setting.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/97466fae-66e9-4b88-8880-ab5aba5372ae/image.png" alt=""></p>
<h4 id="user-모델-생성">User 모델 생성</h4>
<p>파일 경로 ➡️ accounts/models</p>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/d9bbb56c-b9f8-4f9f-99ee-08522c587726/image.png" alt=""></p>
<p>파일 경로 ➡️ settings.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/055fd834-4331-4c2b-a9de-94d543d77d88/image.png" alt=""></p>
<p>파일 경로 ➡️ accounts/admin.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/8ce0c30b-04ea-4d93-8718-bfcbb1c566df/image.png" alt=""></p>
<h2 id="중요">!중요!</h2>
<ul>
<li>User model을 대체하여 Custom User 클래스를 프로젝트 중간에 바꾸는 것은 번거로운 작업이 수반될 수 있기때문에 항상 프로젝트 시작 시 모두 선언하고 migration을 해주는 것이 좋다.</li>
<li>피치 못한 사정으로 프로젝트 진행 도중 하게될 경우, 이전 앱의 migration 속 &#39;<strong>init</strong>.py&#39;를 제외한 &#39;0001~&#39;형식으로 생긴 파일들을 전부 지워주고, db.sqlite3 까지 모두 지운 후 다시 makemigrations, migrate를 진행 할 것 </li>
</ul>
<h2 id="log-in">[Log-in]</h2>
<p>파일 경로 ➡️ accounts/urls
<img src="https://velog.velcdn.com/images/yoon-dh/post/34f8dcb4-0a9d-44ba-879a-bed007bd5d69/image.png" alt=""></p>
<p>파일 경로 ➡️ accounts/views
<img src="https://velog.velcdn.com/images/yoon-dh/post/a5087838-f5b4-4737-b53f-1473e128ee13/image.png" alt=""></p>
<p>파일 경로 ➡️ accounts/login.html
<img src="https://velog.velcdn.com/images/yoon-dh/post/2842ac27-104b-4d6a-ac37-c9f381d1ade9/image.png" alt=""></p>
<h2 id="log-out">[Log-out]</h2>
<p>파일 경로 ➡️ accounts/urls.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/f9526e4d-32e0-4e47-afa4-dd1a721f8563/image.png" alt=""></p>
<p>파일 경로 ➡️ accounts/views.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/b0c848d5-f380-4cfc-9251-291bb7549d1f/image.png" alt=""></p>
<p>파일 경로 ➡️ base.html
<img src="https://velog.velcdn.com/images/yoon-dh/post/bf37f9b4-fb2c-4405-8df4-a01f1e023e2d/image.png" alt=""></p>
<h2 id="회원-가입">[회원 가입]</h2>
<p>파일 경로 ➡️ base.html
<img src="https://velog.velcdn.com/images/yoon-dh/post/d83e910f-0057-453c-ab7b-909311e129e3/image.png" alt=""></p>
<ul>
<li>회원가입 버튼 base.html에 생성</li>
</ul>
<p>파일 경로 ➡️ accounts/forms.py(new file)
<img src="https://velog.velcdn.com/images/yoon-dh/post/b9db3acd-8e1d-4dd3-bce4-88ac8b619d5f/image.png" alt=""></p>
<ul>
<li>Custom User model 사용을 위해 확장 
파일 경로 ➡️ accounts/urls.py</li>
</ul>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/7c5ce452-f2df-430d-a3d0-c466154c4581/image.png" alt=""></p>
<p>파일 경로 ➡️ accounts/views.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/4123670d-2eae-4938-9cc5-b4f1f0c9cd85/image.png" alt=""></p>
<p>파일 경로 ➡️ accounts/signup.html
<img src="https://velog.velcdn.com/images/yoon-dh/post/a5955788-0432-414c-a367-d04af85a01a4/image.png" alt=""></p>
<h2 id="회원-탈퇴">[회원 탈퇴]</h2>
<p>파일 경로 ➡️ accounts/base.html
<img src="https://velog.velcdn.com/images/yoon-dh/post/6e80f9ec-620c-430c-b425-6867b529b785/image.png" alt=""></p>
<p>파일 경로 ➡️ accounts/urls.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/51687f6e-19be-4e15-9818-51f071156426/image.png" alt=""></p>
<p>파일 경로 ➡️ accounts/views.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/c463e2dc-2786-45d5-8e53-99d62ba57035/image.png" alt=""></p>
<h2 id="회원-정보-수정">[회원 정보 수정]</h2>
<p>파일 경로 ➡️ base.html
<img src="https://velog.velcdn.com/images/yoon-dh/post/dc19fd05-b58c-4687-9d78-9b657196400e/image.png" alt=""></p>
<ul>
<li>회원정보수정 버튼 생성</li>
</ul>
<p>파일 경로 ➡️ accounts/forms.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/e0249940-e606-413a-bccd-37a11cb2d648/image.png" alt=""></p>
<p>파일 경로 ➡️ accounts/urls.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/b014ca22-59af-4d5a-9e9a-fa4a4f13fc11/image.png" alt=""></p>
<p>파일 경로 ➡️ accounts/views.py
<img src="https://velog.velcdn.com/images/yoon-dh/post/de711227-4076-4f28-9bba-baaab3367428/image.png" alt=""></p>
<p>파일 경로 ➡️ accounts/updates.html
<img src="https://velog.velcdn.com/images/yoon-dh/post/5bcf6a60-c8f4-4f87-95ad-3f73c8f7d4ec/image.png" alt=""></p>
<h2 id="loginlogout-status">[login&amp;logout status]</h2>
<p><img src="https://velog.velcdn.com/images/yoon-dh/post/b02eec1d-8b1f-487d-9caa-ac9aa37b2fcd/image.png" alt=""></p>
<ul>
<li>login 된 상태 : 로그아웃,회원정보수정,회원탈퇴 show</li>
<li>logout 된 상태 : 로그인, 회원가입 show</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>