<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>haeng_9.log</title>
        <link>https://velog.io/</link>
        <description>Data Engineer🐣</description>
        <lastBuildDate>Thu, 22 Feb 2024 01:48:28 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>haeng_9.log</title>
            <url>https://velog.velcdn.com/images/haeng_9/profile/9857cd2b-fcee-436b-846a-0e5efd9bb4f1/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. haeng_9.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/haeng_9" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[TIL | 240222] React - UI 구성하기]]></title>
            <link>https://velog.io/@haeng_9/TIL-240222-React-UI-%EA%B5%AC%EC%84%B1%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@haeng_9/TIL-240222-React-UI-%EA%B5%AC%EC%84%B1%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 22 Feb 2024 01:48:28 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>공부 자료 : <a href="https://react.dev/learn">https://react.dev/learn</a></p>
</blockquote>
<h1 id="describing-the-ui"><a href="https://react.dev/learn/describing-the-ui">Describing the UI</a></h1>
<hr>
<blockquote>
<p>React는 사용자 인터페이스(UI)를 렌더링하기 위한 JavaScript 라이브러리</p>
</blockquote>
<ul>
<li>배울 내용 : React 컴포넌트 생성, 사용자 정의, 조건부 표시</li>
<li>UI → 버튼, 텍스트, 이미지와 같은 작은 단위로 구성</li>
<li>React를 사용하면 재사용 가능, 중첩 가능한 컴포넌트로 결합 가능</li>
</ul>
<h2 id="1-your-first-component">1. Your First Component</h2>
<h3 id="component"><em>Component</em></h3>
<ul>
<li>markup으로 사용할 수 있는 JavaScript 함수</li>
<li>React application은 컴포넌트들로 구축되어짐.</li>
<li>button과 같은 작은 것일 수도 있고, 전체 페이지일 수도 있음.</li>
<li>재사용 가능한 UI 구성 요소</li>
<li>HTML 태그와 마찬가지로 컴포넌트를 작성하고, 순서를 정하고 중첩해서 전체 페이지를 디자인할 수 있음</li>
</ul>
<h3 id="1-define-component">1) Define Component</h3>
<blockquote>
<p>기존에는 웹 페이지를 만들 때 콘텐츠를 마크업하고 JS를 사용해 상호 작용을 추가했음. React를 사용하면 같은 기술을 사용하면서도 상호작용을 우선시 함.</p>
</blockquote>
<ul>
<li>React component는 마크업으로 뿌릴 수 있는 JavaScript 함수</li>
</ul>
<h3 id="2-how-to-build-a-component">2) How to build a Component</h3>
<pre><code>**Step 1: Export the component**</code></pre><p><code>export default</code> - 표준 JS 구문</p>
<pre><code>**Step 2: Define the function**</code></pre><p><code>function 함수명() {}</code> </p>
<pre><code>**Step 3: Add markup**</code></pre><h3 id="3-using-a-component">3) Using a component</h3>
<ul>
<li>다른 컴포넌트에 중첩하여 사용 가능</li>
</ul>
<pre><code class="language-jsx">function Profile() {
  return (
    &lt;img
      src=&quot;https://i.imgur.com/MK3eW3As.jpg&quot;
      alt=&quot;Katherine Johnson&quot;
    /&gt;
  );
}

export default function Gallery() {
  return (
    &lt;section&gt;
      &lt;h1&gt;Amazing scientists&lt;/h1&gt;
      &lt;Profile /&gt;
      &lt;Profile /&gt;
      &lt;Profile /&gt;
    &lt;/section&gt;
  );
}</code></pre>
<h3 id="4-what-the-browser-sees-브라우저에-표시되는-내용">4) What the browser sees (브라우저에 표시되는 내용)</h3>
<ul>
<li>대소문자 차이에 주목</li>
<li>보통 소문자 태그는 HTML 태그</li>
<li>보통 대문자 태그는 컴포넌트</li>
</ul>
<h3 id="5-nesting-and-organizing-componets-컴포넌트-중첩-및-구성">5) Nesting and organizing componets (컴포넌트 중첩 및 구성)</h3>
<ul>
<li>같은 파일에 여러 컴포넌트 포함, 및 분리 가능</li>
<li>한 번 정의 해놓고 필요할 때 마다 재사용 가능</li>
</ul>
<h3 id="summary">Summary</h3>
<ul>
<li>React lets you create components, <strong>reusable UI elements for your app.</strong></li>
<li>In a React app, every piece of UI is a component.</li>
<li>React components are regular JavaScript functions except:<ol>
<li>Their names always begin with a <strong>capital letter.</strong></li>
<li>They return JSX markup.</li>
</ol>
</li>
</ul>
<h2 id="2-importing-and-exporting-componets">2. Importing and Exporting Componets</h2>
<blockquote>
<p>하나의 파일에 여러 컴포넌트를 선언할 수 있지만, 파일이 큰 경우 탐색이 어려우므로 컴포넌트를 자제 파일로 export 해서 다른 파일에서 컴포넌트를 import 하여 사용 가능</p>
</blockquote>
<ul>
<li>component를 조합해 또 다른 component 생성 가능</li>
<li>여러번 중첩하게 될 경우, 파일 분리를 통해 편의성을 높일 수 있음.</li>
</ul>
<h3 id="1-the-root-component-file">1) The root component file</h3>
<pre><code class="language-jsx">function Profile() {
  return (
    &lt;img
      src=&quot;https://i.imgur.com/MK3eW3As.jpg&quot;
      alt=&quot;Katherine Johnson&quot;
    /&gt;
  );
}

export default function Gallery() {
  return (
    &lt;section&gt;
      &lt;h1&gt;Amazing scientists&lt;/h1&gt;
      &lt;Profile /&gt;
      &lt;Profile /&gt;
      &lt;Profile /&gt;
    &lt;/section&gt;
  );
}</code></pre>
<p>위의 코드에서 <code>Profile</code>  , <code>Gallery</code>  컴포넌트는 모두 <code>Apps.js</code>  root componet 파일에 존재.</p>
<ul>
<li>Create React App에서는 앱 전체가 <code>src/App.js</code> 에서 실행</li>
</ul>
<h3 id="2-exporting-and-importing-a-component">2) Exporting and importing a component</h3>
<ul>
<li>컴포넌트 분리를 통해 모듈성 강화 및, 다른 파일에서 재사용 용이하게 할 수 있음</li>
</ul>
<aside>
👉 **[ 컴포넌트 분리 ]**

<ol>
<li><p>Make a new JS file to put the components in.</p>
</li>
<li><p>Export your function component from that file (using dafault, named export)</p>
</li>
<li><p>Import it in the file where you’ll use the component (using the corresponding technique for importing <a href="https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/import#importing_defaults">default</a> or <a href="https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/import#import_a_single_export_from_a_module">named</a> exports).</p>
</li>
</ol>
</aside>

<p><strong>App.js</strong></p>
<pre><code class="language-jsx">import Gallery from &quot;./component/Gallery&quot;;

export default function App() {
  return (
    &lt;Gallery /&gt;
  );
}</code></pre>
<p><strong>Gallery.js</strong></p>
<pre><code class="language-jsx">function Profile() {
    return (
      &lt;img
        src=&quot;https://i.imgur.com/QIrZWGIs.jpg&quot;
        alt=&quot;Alan L. Hart&quot;
      /&gt;
    );
  }

  export default function Gallery() {
    return (
      &lt;section&gt;
        &lt;h1&gt;Amazing scientists&lt;/h1&gt;
        &lt;Profile /&gt;
        &lt;Profile /&gt;
        &lt;Profile /&gt;
      &lt;/section&gt;
    );
  }</code></pre>
<ol>
<li><code>Gallery.js</code>:<ul>
<li>Defines the <code>Profile</code> component which is only used within the same file and is not exported.</li>
<li>Exports the <code>Gallery</code> component as a <strong>default export.</strong></li>
</ul>
</li>
<li><code>App.js</code>:<ul>
<li>Imports <code>Gallery</code> as a <strong>default import</strong> from <code>Gallery.js</code>.</li>
<li>Exports the root <code>App</code> component as a <strong>default export.</strong></li>
</ul>
</li>
</ol>
<h3 id="3-exporting-and-importing-multiple-components-from-the-same-file">3) <strong>Exporting and importing multiple components from the same file</strong></h3>
<ul>
<li><p>하나의 파일은 하나의 default export만 가질 수 있음.</p>
</li>
<li><p>하나의 파일에서 여러 컴포넌트 사용을 위해서는</p>
<p>  1) 새 파일을 만들어 default export 하거나, </p>
<p>  2) named export를 추가</p>
</li>
</ul>
<p><strong>named export</strong></p>
<pre><code class="language-jsx">export function Profile() {
  // ...
}</code></pre>
<pre><code class="language-jsx">import { Profile } from &#39;./Gallery.js&#39;;</code></pre>
<ul>
<li>중괄호를 사용해 import 할 수 있음</li>
</ul>
<h3 id="summary-1">Summary</h3>
<p>On this page you learned:</p>
<ul>
<li>What a root component file is</li>
<li>How to import and export a component</li>
<li>When and how to use default and named imports and exports</li>
<li>How to export multiple components from the same file</li>
</ul>
<h2 id="3-writing-markup-with-jsx">3. Writing Markup with JSX</h2>
<h3 id="jsx">JSX</h3>
<ul>
<li>React Component는 JSX라는 구문 확장자를 사용해 마크업을 표현</li>
<li>HTML과 비슷하지만 더 엄격하고, 동적인 정보를 표시할 수 있음</li>
<li>JavaScript를 확장한 문법으로, JavaScript 파일 안에 HTML과 유사한 마크업을 작성할 수 있게 해줌</li>
</ul>
<h3 id="1-jsx-putting-markup-into-javascript">1) JSX: Putting markup into JavaScript</h3>
<blockquote>
<p>보통 웹을 만들 때 HTML⇒ 컨텐츠, CSS ⇒ 디자인, JavaScript ⇒ 로직. but interactive 해지면서 로직이 컨텐츠를 결정하는 경우가 많아져 렌더링 로직과 마크업이 같은 위치의 컴포넌트에 있게 됨.</p>
</blockquote>
<h3 id="2-converting-html-to-jsx--the-rules-of-jsx">2) Converting HTML to JSX / The Rules of JSX</h3>
<p><strong>1. Return a single root element</strong></p>
<ul>
<li>컴포넌트에서 여러 엘리먼트를 반환할 때 하나의 부모 태그로 감싸야 함</li>
<li>ex) <div></div> , &lt;&gt;, &lt;/&gt;</li>
<li><code>&lt;&gt;, &lt;/&gt;</code> - fragment, 브라우저상 HTML 트리 구조에서 흔적을 남기지 않고 그룹화해줌</li>
</ul>
<blockquote>
<p><strong>JSX태그를 하나로 감싸는 이유?</strong>
⇒ JSX는 HTML처럼 보이지만 내부적으로 JS 객체로 변환되기 때문에 하나의 배열로 감싸야 함.</p>
</blockquote>
<p><strong>2. Close all the tags</strong></p>
<ul>
<li>모든 태그를 닫는 태그로 사용해야 함</li>
<li>ex) <code>&lt;img&gt;</code>  ⇒ <code>&lt;img /&gt;</code></li>
</ul>
<p><strong>3. camelCase <del>all</del> most of the things!</strong></p>
<ul>
<li>변수명에 대시나 예약어 사용 불가</li>
</ul>
<h3 id="3-jsx-변환기-사용">3) JSX 변환기 사용</h3>
<p><a href="https://transform.tools/html-to-jsx">HTML to JSX</a></p>
<h3 id="summary-2">Summary</h3>
<p>Now you know why JSX exists and how to use it in components:</p>
<ul>
<li>React components group rendering logic together with markup because they are related.</li>
<li>JSX is similar to HTML, with a few differences. You can use a <a href="https://transform.tools/html-to-jsx">converter</a> if you need to.</li>
<li>Error messages will often point you in the right direction to fixing your markup.</li>
</ul>
<h2 id="4-javascript-in-jsx-with-curly-braces">4. JavaScript in JSX with Curly Braces</h2>
<blockquote>
<p>JSX를 사용하면 JS 파일 내에 HTML과 유사한 마크업 언어를 작성해 rendering logic과 content를 같은 위치에 둘 수 있음. JSX에서 중괄호를 사용해 마크업 안에 JavaScript 로직을 추가하거나 동적인 property를 참고할 수 있음.</p>
</blockquote>
<h3 id="1-passing-strings-with-quotes-">1) Passing strings with quotes(””, ‘’)</h3>
<ul>
<li>JSX에 문자열 속성을 전달하려면, 작은 따옴표 혹은 큰 따옴표로 묶음</li>
</ul>
<pre><code class="language-jsx">export default function Avatar() {
  return (
    &lt;img
      className=&quot;avatar&quot;
      src=&quot;https://i.imgur.com/7vQD0fPs.jpg&quot;
      alt=&quot;Gregorio Y. Zara&quot;
    /&gt;
  );
}</code></pre>
<ul>
<li><code>{}</code>  : <code>&quot; &quot;</code> 대신 사용. 동적으로 지정해 JS 값 사용 가능</li>
</ul>
<h3 id="2-using-curly-braces--중괄호-사용-위치">2) Using curly braces / 중괄호 사용 위치</h3>
<ul>
<li><code>{}</code>  중괄호 사이에는 함수 호출을 포함한 모든 JS 표현식이 작동</li>
</ul>
<p><strong>1. JSX 태그 안에 직접 텍스트로 사용</strong></p>
<p><code>&lt;h1&gt;{name}&#39;s To Do List&lt;/h1&gt;</code> ⇒ O</p>
<p><code>&lt;{tag}&gt;Gregorio Y. Zara&#39;s To Do List&lt;/{tag}&gt;</code> ⇒ X</p>
<p><strong>2. <code>=</code> 기호 바로 뒤에 오는 속성</strong></p>
<p><code>src={avatar}</code> ⇒ 아바타 변수</p>
<p><code>src=&quot;{avatar}&quot;</code> ⇒ 문자열 “{avatar}”</p>
<h3 id="3-using-double-curlies-css-and-other-objects-in-jsx">3) Using “double curlies”: CSS and other objects in JSX</h3>
<ul>
<li><code>{{ }}</code> - JSX에서 JS 객체 전달, JSX 중괄호 내부의 객체</li>
<li>ex) 인라인 스타일이 필요한 경우 style attribute에 객체 전달 <code>style={{ }}</code></li>
</ul>
<pre><code class="language-jsx">export default function TodoList() {
  return (
    &lt;ul style={{
      backgroundColor: &#39;black&#39;,
      color: &#39;pink&#39;
    }}&gt;
      &lt;li&gt;Improve the videophone&lt;/li&gt;
      &lt;li&gt;Prepare aeronautics lectures&lt;/li&gt;
      &lt;li&gt;Work on the alcohol-fuelled engine&lt;/li&gt;
    &lt;/ul&gt;
  );
}</code></pre>
<h3 id="4-more-fun-with-javascript-objects-and-curly-braces">4) <strong>More fun with JavaScript objects and curly braces</strong></h3>
<ul>
<li>여러 표현식을 하나의 객체로 이동해 중괄호 안에 있는 JSX에서 참조할 수 있음</li>
</ul>
<pre><code class="language-jsx">const person = {
  name: &#39;Gregorio Y. Zara&#39;,
  theme: {
    backgroundColor: &#39;black&#39;,
    color: &#39;pink&#39;
  }
};

export default function TodoList() {
  return (
    &lt;div style={person.theme}&gt;
      &lt;h1&gt;{person.name}&#39;s Todos&lt;/h1&gt;
      &lt;img
        className=&quot;avatar&quot;
        src=&quot;https://i.imgur.com/7vQD0fPs.jpg&quot;
        alt=&quot;Gregorio Y. Zara&quot;
      /&gt;
      &lt;ul&gt;
        &lt;li&gt;Improve the videophone&lt;/li&gt;
        &lt;li&gt;Prepare aeronautics lectures&lt;/li&gt;
        &lt;li&gt;Work on the alcohol-fuelled engine&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/div&gt;
  );</code></pre>
<h3 id="summary-3">Summary</h3>
<p>Now you know almost everything about JSX:</p>
<ul>
<li>JSX attributes inside quotes are passed as strings.</li>
<li>Curly braces let you bring JavaScript logic and variables into your markup.</li>
<li>They work inside the JSX tag content or immediately after <code>=</code> in attributes.</li>
<li><code>{{</code> and <code>}}</code> is not special syntax: it’s a JavaScript object tucked inside JSX curly braces.</li>
<li>따옴표 안의 JSX 속성은 문자열로 전달됩니다.</li>
<li>중괄호를 사용하면 JavaScript 로직과 변수를 마크업으로 가져올 수 있습니다.</li>
<li>중괄호는 JSX 태그 콘텐츠 내부 또는 속성의 <code>=</code> 바로 뒤에서 작동합니다.</li>
<li><code>{{</code> 와 <code>}}</code> 는 특별한 구문이 아니라 JSX 중괄호 안에 들어 있는 JavaScript 객체입니다.</li>
</ul>
<h2 id="5-passing-props-to-a-component">5. Passing Props to a Component</h2>
<h3 id="1-props">1) Props</h3>
<ul>
<li>component 간 통신을 위해 사용</li>
<li>부모 component는 자식 component에 props를 전달해 정보를 줄 수 있음</li>
<li>HTML attributes, 객체, 배열, 함수, JSX를 포함한 JavaScript 값 전달 가능</li>
<li>JSX 태그에 전달하는 정보<ul>
<li>ex) <code>className</code>, <code>src</code>, <code>alt</code>, <code>width</code>, <code>height</code> 는 <code>&lt;img&gt;</code> 태그에 전달, 그 외 어떤 컴포넌트에도 props 전달 가능</li>
</ul>
</li>
</ul>
<h3 id="2-passing-props-to-a-component-step">2) Passing props to a component/ Step</h3>
<p><strong>Step 1: Pass props to the child component (자식 컴포넌트에 props 전달)</strong></p>
<pre><code class="language-jsx">export default function Profile() {
  return (
    &lt;Avatar
      person={{ name: &#39;Lin Lanying&#39;, imageId: &#39;1bX5QH6&#39; }}
      size={100}
    /&gt;
  );
}</code></pre>
<p><strong>Step 2: Read props inside the child component (자식 컴포넌트 내부에서 props 읽기)</strong></p>
<pre><code class="language-jsx">function Avatar({ person, size }) {
  // person and size are available here
}</code></pre>
<p>🚨 props 선언 시 <code>()</code> 안에 <code>{ }</code> 잊지 않기</p>
<h3 id="3-props---default-value">3) Props - default value</h3>
<ul>
<li><p>변수 뒤에 <code>= 기본값</code></p>
<pre><code class="language-jsx">  function Avatar({ person, size = 100 }) {
    // ...
  }</code></pre>
</li>
<li><p>prop이 없거나 undefined인 경우 ( null, 0인 경우는 X)</p>
</li>
</ul>
<h3 id="4-jsx-spread-syntax">4) JSX spread syntax</h3>
<ul>
<li>모든 props를 한번에 전달</li>
</ul>
<pre><code class="language-jsx">function Profile({ person, size, isSepia, thickBorder }) {
  return (
    &lt;div className=&quot;card&quot;&gt;
      &lt;Avatar
        person={person}
        size={size}
        isSepia={isSepia}
        thickBorder={thickBorder}
      /&gt;
    &lt;/div&gt;
  );
}</code></pre>
<pre><code class="language-jsx">function Profile(props) {
  return (
    &lt;div className=&quot;card&quot;&gt;
      &lt;Avatar {...props} /&gt;
    &lt;/div&gt;
  );
}</code></pre>
<h3 id="5-component-중첩">5) component 중첩</h3>
<pre><code class="language-jsx">&lt;Card&gt;
    &lt;Avatar /&gt;
&lt;/Card&gt;</code></pre>
<ul>
<li>JSX 태그 내 콘텐츠 중첩을 하면 부모 컴포넌트는 해당 컨텐츠를 <code>children</code> 이라는 prop으로 받음</li>
</ul>
<h3 id="summary-4">Summary</h3>
<ul>
<li>To pass props, add them to the JSX, just like you would with HTML attributes.</li>
<li>To read props, use the <code>function Avatar({ person, size })</code> destructuring syntax.</li>
<li>You can specify a default value like <code>size = 100</code>, which is used for missing and <code>undefined</code> props</li>
<li>You can forward all props with <code>&lt;Avatar {...props} /&gt;</code> JSX spread syntax, but don’t overuse it!</li>
<li>Nested JSX like <code>&lt;Card&gt;&lt;Avatar /&gt;&lt;/Card&gt;</code> will appear as <code>Card</code> component’s <code>children</code> prop.</li>
<li>Props are read-only snapshots in time: every render receives a new version of props.</li>
<li>You can’t change props. When you need interactivity, you’ll need to set state.<ul>
<li>Props는 변경할 수 없습니다. 상호작용이 필요한 경우 state를 설정해야 합니다.</li>
</ul>
</li>
</ul>
<h2 id="6-conditional-rendering">6. Conditional Rendering</h2>
<blockquote>
<p><code>if문</code> , <code>&amp;&amp;</code> , <code>? :</code> 와 같은 JavaScript 구문을 사용해 조건부로 JSX를 렌더링할 수 있음.</p>
</blockquote>
<h3 id="1-if--else-를-사용한-조건부-렌더링">1) If / else 를 사용한 조건부 렌더링</h3>
<pre><code class="language-jsx">if (isPacked) {
  return &lt;li className=&quot;item&quot;&gt;{name} ✔&lt;/li&gt;;
}
return &lt;li className=&quot;item&quot;&gt;{name}&lt;/li&gt;;</code></pre>
<ul>
<li>아무 것도 반환하지 않을 때 <code>null</code> 사용<ul>
<li><code>return null;</code></li>
</ul>
</li>
</ul>
<h3 id="2-conditionalternary-operator-------삼항연산자">2) Conditional(ternary) operator (<code>? :</code>  ) - 삼항연산자</h3>
<pre><code class="language-jsx">if (isPacked) {
  return &lt;li className=&quot;item&quot;&gt;{name} ✔&lt;/li&gt;;
}
return &lt;li className=&quot;item&quot;&gt;{name}&lt;/li&gt;;</code></pre>
<pre><code class="language-jsx">return (
  &lt;li className=&quot;item&quot;&gt;
    {isPacked ? name + &#39; ✔&#39; : name}
  &lt;/li&gt;
);</code></pre>
<ul>
<li><code>con ? A : B</code> ⇒ condition 이 true이면 A 렌더링, false이면 B 렌더링</li>
</ul>
<h3 id="3-논리-and-연산자-">3) 논리 AND 연산자 (<code>&amp;&amp;</code>)</h3>
<pre><code class="language-jsx">return (
  &lt;li className=&quot;item&quot;&gt;
    {name} {isPacked &amp;&amp; &#39;✔&#39;}
  &lt;/li&gt;
);</code></pre>
<ul>
<li>조건이 참일 때 렌더링, 그렇지 않으면(false), 아무 것도 렌더링 하지 않음</li>
</ul>
<h3 id="summary-5">Summary</h3>
<ul>
<li>In React, you control branching logic with JavaScript.</li>
<li>You can return a JSX expression conditionally with an <code>if</code> statement.</li>
<li>You can conditionally save some JSX to a variable and then include it inside other JSX by using the curly braces.</li>
<li>In JSX, <code>{cond ? &lt;A /&gt; : &lt;B /&gt;}</code> means <em>“if <code>cond</code>, render <code>&lt;A /&gt;</code>, otherwise <code>&lt;B /&gt;</code>”</em>.</li>
<li>In JSX, <code>{cond &amp;&amp; &lt;A /&gt;}</code> means <em>“if <code>cond</code>, render <code>&lt;A /&gt;</code>, otherwise nothing”</em>.</li>
<li>The shortcuts are common, but you don’t have to use them if you prefer plain <code>if</code>.</li>
</ul>
<h2 id="7-rendering-lists">7. Rendering Lists</h2>
<blockquote>
<p>JavaScript의 <code>filter()</code> , <code>map()</code> 을 사용해 데이터 배열을 필터링하고 component의 배열로 변환할 수 있음. 각 배열 항목마다 <code>key</code> 를 지정해 목록이 변경되더라도 목록에서 각 항목의 위치 추적 가능</p>
</blockquote>
<h3 id="1-rendering-data-from-arrays">1) Rendering data from arrays</h3>
<ol>
<li><p>Move the data into an array</p>
<pre><code class="language-jsx"> const people = [
   &#39;Creola Katherine Johnson: mathematician&#39;,
   &#39;Mario José Molina-Pasquel Henríquez: chemist&#39;,
   &#39;Mohammad Abdus Salam: physicist&#39;,
   &#39;Percy Lavon Julian: chemist&#39;,
   &#39;Subrahmanyan Chandrasekhar: astrophysicist&#39;
 ];</code></pre>
</li>
<li><p>Map the data into a new array of JSX nodes</p>
<pre><code class="language-jsx"> const listItems = people.map(person =&gt; &lt;li&gt;{person}&lt;/li&gt;);</code></pre>
</li>
<li><p>Return new array from the component </p>
<pre><code class="language-jsx"> return &lt;ul&gt;{listItems}&lt;/ul&gt;;</code></pre>
</li>
</ol>
<h3 id="2-filtering-arrays-of-items">2) Filtering arrays of items</h3>
<ol>
<li><p>Create a new array of some conditions, by calling <code>filter()</code> on the array filtering by conditions.</p>
<pre><code class="language-jsx"> const chemists = people.filter(person =&gt;
   person.profession === &#39;chemist&#39;
 );</code></pre>
</li>
<li><p>map over new array</p>
<pre><code class="language-jsx"> const listItems = chemists.map(person =&gt;
   &lt;li&gt;
      &lt;img
        src={getImageUrl(person)}
        alt={person.name}
      /&gt;
      &lt;p&gt;
        &lt;b&gt;{person.name}:&lt;/b&gt;
        {&#39; &#39; + person.profession + &#39; &#39;}
        known for {person.accomplishment}
      &lt;/p&gt;
   &lt;/li&gt;
 );</code></pre>
</li>
<li><p>return the mapped array from the component</p>
<pre><code class="language-jsx"> return &lt;ul&gt;{listItems}&lt;/ul&gt;;</code></pre>
 <aside>
 👉 화살표 함수(`⇒`)는 바로 뒤에 표현식을 암시적으로 Reurn하므로 `return` 문이 필요하지 않음.
 하지만 중괄호(`{}`)가 오는 경우는 `return` 문 필요.

 </aside>


</li>
</ol>
<h3 id="3-about-key">3) About <code>key</code></h3>
<ul>
<li>map각 배열의 항목들을 고유하게 식별할 수 있는 문자열 또는 숫자인 key를 부여해야 함.</li>
<li>key는 각 컴포넌트가 어떤 배열 항목에 해당하는지 알려줘 매칭하도록 함.</li>
<li>따로 지정하지 않으면 기본적으로 Index를 Key로 사용하지만 새 항목 삽입, 삭제, 배열 순서가 바뀌면서 변경될 수 있음</li>
</ul>
<p><strong>key를 얻는 방법</strong></p>
<ol>
<li>Database 데이터 : 고유한 DB key/ID 사용 </li>
<li>Local 데이터 : incrementing counter, <code>crypto.randomUUID()</code> , <code>uuid</code> 패키지 사용</li>
</ol>
<p><strong>key 규칙</strong></p>
<ul>
<li>같은 배열 JSX 노드에는 고유해야 함.</li>
<li>Key는 변경되지 않아야 함 (렌더링 중 생성 금지)</li>
</ul>
<h3 id="summary-6">Summary</h3>
<ul>
<li>How to move data out of components and into data structures like arrays and objects.</li>
<li>How to generate sets of similar components with JavaScript’s <code>map()</code>.</li>
<li>How to create arrays of filtered items with JavaScript’s <code>filter()</code>.</li>
<li>Why and how to set <code>key</code> on each component in a collection so React can keep track of each of them even if their position or data changes.</li>
</ul>
<h2 id="8-keeping-components-pure">8. Keeping Components Pure</h2>
<h3 id="pure-function">Pure function</h3>
<ol>
<li>자신의 일만 처리 - 호출 전 존재했던 객체나 변수를 변경하지 않음</li>
<li>same inputs, same outputs - 동일 입력에는 항상 동일 결과 반환</li>
</ol>
<ul>
<li>버그나 예측 불가한 동작이 발생하는 것을 피할 수 있음.</li>
</ul>
<h3 id="1-purity-components-as-formulas">1) <strong>Purity: Components as formulas</strong></h3>
<ul>
<li>React는 모든 컴포넌트가 pure function 이라고 가정.</li>
</ul>
<h3 id="2-side-effects-unintended-consequences">2) <strong>Side Effects: (un)intended consequences</strong></h3>
<ul>
<li>컴포넌트는 JSX만을 반환해야 하고, 렌더링 전 존재하는 객체, 변수를 변경해서는 안됨</li>
</ul>
<p>[순수성을 어기는 예시]</p>
<pre><code class="language-jsx">let guest = 0;

function Cup() {
  // Bad: changing a preexisting variable!
  // 나쁨: 기존 변수를 변경합니다!
  guest = guest + 1;
  return &lt;h2&gt;Tea cup for guest #{guest}&lt;/h2&gt;;
}

export default function TeaSet() {
  return (
    &lt;&gt;
      &lt;Cup /&gt;
      &lt;Cup /&gt;
      &lt;Cup /&gt;
    &lt;/&gt;
  );
}</code></pre>
<p>[수정]</p>
<pre><code class="language-jsx">function Cup({ guest }) {
  return &lt;h2&gt;Tea cup for guest #{guest}&lt;/h2&gt;;
}

export default function TeaSet() {
  return (
    &lt;&gt;
      &lt;Cup guest={1} /&gt;
      &lt;Cup guest={2} /&gt;
      &lt;Cup guest={3} /&gt;
    &lt;/&gt;
  );
}</code></pre>
<h3 id="3-local-mutation-your-components-little-secret">3) <strong>Local mutation: Your component’s little secret</strong></h3>
<ul>
<li><p>렌더링 전에 존재하는 것을 변경하는 것은 안되지만 렌더링 하는 동안 ‘방금’ 생성한 변수와 객체를 변경하는 것은 가능</p>
<pre><code class="language-jsx">  function Cup({ guest }) {
    return &lt;h2&gt;Tea cup for guest #{guest}&lt;/h2&gt;;
  }

  export default function TeaGathering() {
    let cups = [];
    for (let i = 1; i &lt;= 12; i++) {
      cups.push(&lt;Cup key={i} guest={i} /&gt;);
    }
    return cups;
  }</code></pre>
</li>
</ul>
<h3 id="4-where-you-can-cause-side-effects">4) <strong>Where you <em>can</em> cause side effects</strong></h3>
<ul>
<li>화면 업데이트, 애니메이션 시작, 데이터 변경과 같은 변경을 side effect라고 함. <strong>렌더링 중이 아닌 ‘부수적으로’</strong> 일어나는 일</li>
</ul>
<p><strong>event handler</strong></p>
<ul>
<li>대게 React에서 사이트 이펙트는 event handler에 속함</li>
<li>사용자가 어떤 동작 수행할 때, React가 실행하는 함수</li>
<li>컴포넌트 내부에 정의되어 있지만 렌더링 중에는 실행되지 않음</li>
<li>적절한 event handler가 없다면 <code>useEffect</code> 사용</li>
</ul>
<h3 id="summary-7">Summary</h3>
<ul>
<li>A component must be pure, meaning:<ul>
<li><strong>It minds its own business.</strong> It should not change any objects or variables that existed before rendering.</li>
<li><strong>Same inputs, same output.</strong> Given the same inputs, a component should always return the same JSX.</li>
</ul>
</li>
<li>Rendering can happen at any time, so components should <strong><em>not depend on each others’ rendering sequence.</em></strong></li>
<li>You should not mutate any of the inputs that your components use for rendering. That includes props, state, and context. To update the screen, <a href="https://react-ko.dev/learn/state-a-components-memory">“set” state</a> instead of mutating preexisting objects.</li>
<li>Strive to express your component’s logic in the JSX you return. When you need to “change things”, you’ll usually want to do it in an event handler. As a last resort, you can <code>useEffect</code>.</li>
<li>Writing pure functions takes a bit of practice, but it unlocks the power of React’s paradigm.</li>
</ul>
<h2 id="9-understanding-your-ui-as-a-tree">9. Understanding Your UI as a Tree</h2>
<blockquote>
<p>React와 많은 UI 라이브러리는 UI를 트리로 모델링</p>
</blockquote>
<ul>
<li>Tree ⇒ relationship model ex)브라우저는 HTML(DOM), CSS(CSSOM) 모델링을 위해 트리 구조 사용</li>
</ul>
<h3 id="the-render-tree-렌더링할-때-사용하는-트리-개념">The Render Tree (렌더링할 때 사용하는 트리 개념)</h3>
<ul>
<li><p>React app을 rendering 할 때 render tree로 이 관계들을 모델링 할 수 있음.</p>
</li>
<li><p>컴포넌트의 주요 특징 : 다른 컴포넌트의 컴포넌트를 구성할 수 있음</p>
</li>
<li><p>컴포넌트 중첩 시 parent component, child component 개념이 생김</p>
</li>
<li><p>Root node = App의 Root 컴포넌트</p>
</li>
<li><p>node = component, branch = render</p>
</li>
<li><p>conditional rendering ⇒ 부모 컴포넌트가 전달된 데이터에 따라 다른 child component를 렌더링</p>
<pre><code class="language-jsx">  import * as React from &#39;react&#39;;
  import inspirations from &#39;./inspirations&#39;;
  import FancyText from &#39;./FancyText&#39;;
  import Color from &#39;./Color&#39;;

  export default function InspirationGenerator({children}) {
    const [index, setIndex] = React.useState(0);
    const inspiration = inspirations[index];
    const next = () =&gt; setIndex((index + 1) % inspirations.length);

    return (
      &lt;&gt;
        &lt;p&gt;Your inspirational {inspiration.type} is:&lt;/p&gt;
        {inspiration.type === &#39;quote&#39;
        ? &lt;FancyText text={inspiration.value} /&gt;
        : &lt;Color value={inspiration.value} /&gt;}

        &lt;button onClick={next}&gt;Inspire me again&lt;/button&gt;
        {children}
      &lt;/&gt;
    );
  }</code></pre>
</li>
</ul>
<h3 id="the-module-dependency-tree">The Module Dependency Tree</h3>
<ul>
<li>모듈간의 의존성 관계를 나타낼 때 사용하는 트리</li>
<li>컴포넌트를 분리하고 로직을 별도의 파일로 분리하면, 컴포넌트, 함수, 상수를 내보내느 JS 모듈을 만들 수 있음</li>
<li>Root node = root module, 일반적으로 루트 컴포넌트 포함하는 모듈</li>
<li>node = module, branch = <code>import</code></li>
<li>module dependency tree는 컴포넌트가 아닌 모듈도 트리에 나타냄</li>
</ul>
<h3 id="summary-8">Summary</h3>
<ul>
<li>Trees are a common way to represent the relationship between entities. They are often used to model UI.</li>
<li>Render trees represent the nested relationship between React components across a single render.</li>
<li>With conditional rendering, the render tree may change across different renders. With different prop values, components may render different children components.</li>
<li>Render trees help identify what the top-level and leaf components are. Top-level components affect the rendering performance of all components beneath them and leaf components are often re-rendered frequently. Identifying them is useful for understanding and debugging rendering performance.</li>
<li>Dependency trees represent the module dependencies in a React app.</li>
<li>Dependency trees are used by build tools to bundle the necessary code to ship an app.</li>
<li>Dependency trees are useful for debugging large bundle sizes that slow time to paint and expose opportunities for optimizing what code is bundled.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL | 240131] JavaScript - 코딩앙마 왕 초보 자바스크립트]]></title>
            <link>https://velog.io/@haeng_9/TIL-240131-JavaScript-%EC%BD%94%EB%94%A9%EC%95%99%EB%A7%88-%EC%99%95-%EC%B4%88%EB%B3%B4-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8</link>
            <guid>https://velog.io/@haeng_9/TIL-240131-JavaScript-%EC%BD%94%EB%94%A9%EC%95%99%EB%A7%88-%EC%99%95-%EC%B4%88%EB%B3%B4-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8</guid>
            <pubDate>Wed, 31 Jan 2024 05:02:20 GMT</pubDate>
            <description><![CDATA[<h2 id="javascript">Javascript</h2>
<blockquote>
<p>자료 : [코딩앙마] 왕 초보 자바스크립트</p>
</blockquote>
<h3 id="1-변수">1. 변수</h3>
<ul>
<li><p>alert() : 경고창을 찍는 함수</p>
</li>
<li><p>console.log() : 로그를 찍는 함수</p>
</li>
<li><p>let : 최초로 선언하는 모든 변수에 붙여 중복을 방지, 변할 수 있음 </p>
</li>
<li><p>const : 절대로 바뀌지 않는 상수</p>
</li>
</ul>
<h3 id="2-자료형">2. 자료형</h3>
<ul>
<li><p>문자형 : &#39; &#39;, &quot; &quot;, <code></code></p>
</li>
<li><p>숫자형 : 사칙연산 가능 (0으로 나누면 무한대, 문자를 숫자로 나누면 NaN)</p>
</li>
<li><p>bool형 : true, false</p>
</li>
<li><p>null -&gt; 존재하지 않는 값</p>
</li>
<li><p>undefined -&gt; 할당되지 않은 값</p>
</li>
<li><p>객체형</p>
</li>
<li><p>심볼형</p>
</li>
<li><p>typeof 연산자 : 변수의 자료형 확인</p>
</li>
<li><p>숫자형 + 문자형 =&gt; 숫자형이 문자형으로 변환</p>
</li>
</ul>
<h3 id="3-alert-prompt-confirm">3. alert, prompt, confirm</h3>
<p><strong>alert</strong></p>
<ul>
<li>알려줌</li>
<li>메세지를 띄우고 확인 용도</li>
</ul>
<p><strong>prompt</strong></p>
<ul>
<li>입력 받음</li>
<li>prompt(&quot;message&quot;, &quot;default&quot;)</li>
<li>문자형으로 </li>
</ul>
<p><strong>confirm</strong></p>
<ul>
<li>확인 받음</li>
<li>확인, 취소(null 할당) 버튼이 있음</li>
<li>사용자 액션 확인시 자주 사용</li>
</ul>
<p>=&gt; 
장점:
    빠르고 간단함
단점:
    1. 스크립트 일시 정지
    2. 스타일링 X</p>
<h3 id="4-형변환-명시적">4. 형변환 (명시적)</h3>
<ul>
<li>자동 형변환</li>
</ul>
<p><strong>String()</strong></p>
<ul>
<li>문자형으로 변환</li>
</ul>
<p><strong>Number()</strong></p>
<ul>
<li>숫자형으로 변환</li>
<li>문자가 들어있으면 NaN</li>
</ul>
<p><strong>Boolean()</strong></p>
<ul>
<li>불린형으로 변환</li>
<li>0, 빈 문자열, null, undefined, NaN =&gt; false</li>
</ul>
<p><em>!주의</em></p>
<pre><code> Number(null) =&gt; 0
 Number(undefined) =&gt; NaN</code></pre><h3 id="5-기본-연산자">5. 기본 연산자</h3>
<ul>
<li>+, -, *, /</li>
<li>// - 나머지</li>
<li>** - 거듭제곱</li>
<li>++, -- - 증가연산자, 감소연산자</li>
</ul>
<h3 id="6-비교-연산자-조건문-if-else">6. 비교 연산자, 조건문 (if, else)</h3>
<ul>
<li>비교 연산자 - &lt;, &gt;, &lt;= , &gt;=, ==(동등 연산자), !=</li>
<li>=== : 일치 연산자, 타입까지 비교 ( 1 == &#39;1&#39; -&gt; true 로 출력됨)</li>
<li>조건문 - if, else, else if<pre><code>if(조건){
  조건이 참일 때 실행될 부분
} else if (조건1) {
  조건1이 참일 때 실행될 부분
} else {
  조건을 모두 만족하지 못할 때 실행될 부분
}</code></pre></li>
</ul>
<h3 id="7-논리-연산자-and-or-not">7. 논리 연산자 (AND, OR, NOT)</h3>
<ul>
<li>|| - OR</li>
<li>&amp;&amp; - AND</li>
<li>! - NOT
=&gt; 평가 시 성능 최적화(필터링)할 때 활용될 수 있음.</li>
<li>우선 순위 : AND &gt; OR</li>
</ul>
<p>🌟 여기서 기존에 알고 있던 논리 연산자의 사용이 예제 코드를 따라하다가 머리를 크게 맞은 느낌이었다</p>
<pre><code class="language-javascript">// or 연산자 활용 
function sayHello2(name) {
    let newName = name || &#39;friend&#39;; // 매개변수를 입력하지 않으면 true값인 &#39;friend&#39; 반환
    let msg3 = `Hello, ${newName}`
    console.log(msg3);
}</code></pre>
<p>여기서 <code>let newName = name || &#39;friend&#39;</code>  논리 사용자를 사용한 부분에는 당연히 true나 false가 반환값으로 나올 줄 알았는데 or 연산자의 경우 true가 되는 부분의 값이 출렸되었다. 파이썬을 쓰면서 이런 방식은 한 번도 쓰지 않아 이렇게 쓰이는 지 몰랐다🤣</p>
<p>아래 두 링크에서 이를 잘 설명해주고 있다!</p>
<blockquote>
<p><a href="https://velog.io/@proshy/JS%EC%9E%98%EB%AA%BB-%EC%95%8C%EA%B3%A0-%EC%9E%88%EC%97%88%EB%8D%98-%EB%85%BC%EB%A6%AC%EC%97%B0%EC%82%B0%EC%9E%90">https://velog.io/@proshy/JS%EC%9E%98%EB%AA%BB-%EC%95%8C%EA%B3%A0-%EC%9E%88%EC%97%88%EB%8D%98-%EB%85%BC%EB%A6%AC%EC%97%B0%EC%82%B0%EC%9E%90</a></p>
</blockquote>
<blockquote>
<p><a href="https://ella951230.tistory.com/entry/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-truthy-falsy-%EB%8B%A8%EC%B6%95%ED%8F%89%EA%B0%80%EB%8B%A8%EB%9D%BD%ED%9A%8C%EB%A1%9C%ED%8F%89%EA%B0%80">https://ella951230.tistory.com/entry/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-truthy-falsy-%EB%8B%A8%EC%B6%95%ED%8F%89%EA%B0%80%EB%8B%A8%EB%9D%BD%ED%9A%8C%EB%A1%9C%ED%8F%89%EA%B0%80</a></p>
</blockquote>
<h3 id="8-반복문-for-while-do-while">8. 반복문 (for, while, do while)</h3>
<ol>
<li>for문<pre><code>for (초기값; 조건; 코드 실행 후 작업) {
 반복할 코드
}</code></pre></li>
<li>while문<pre><code>while (조건) {
 조건이 참일 동안 반복할 코드
}</code></pre></li>
<li>do while문<pre><code>do {
 무조건 한 번 실행 후, 조건이 참일 동안 반복
} while (조건)</code></pre></li>
<li>break : 멈추고 빠져나옴</li>
<li>continue : 멈추고 다음 반복으로 진행</li>
</ol>
<h3 id="9-switch문">9. switch문</h3>
<ul>
<li>case가 다양할 경우 사용<pre><code>switch (평가) {
  case A:
      A일 때 코드
      break;
  case B:
      B일 때 코드
      break;
  default:
      case에 만족하는 게 없을 때 실행할 코드
}</code></pre></li>
</ul>
<h3 id="10-함수function의-기초">10. 함수(function)의 기초</h3>
<pre><code>function 함수명(매개변수){
    함수 실행 코드
}</code></pre><ul>
<li>중복을 줄여줌</li>
<li>지역 변수를 외부에서도 사용하고 싶으면 밖으로 빼줘야 함</li>
<li>let은 같은 이름의 변수에 대해 한 번만 지정해야 하지만, 전역 변수와 지역 변수는 독립적으로 사용될 수 있음.</li>
<li>return을 함수 종료 목적으로 사용하기도 함.</li>
</ul>
<h3 id="11-함수-표현식-화살표-함수arrow-function">11. 함수 표현식, 화살표 함수(arrow function)</h3>
<ul>
<li>함수 선언문 -&gt; 기존의 함수, 어디서든 호출 가능 (호이스팅)</li>
<li>함수 표현식 -&gt; 함수 이름을 짓지 않고 변수에 할당, 코드에 도달하면 생성</li>
<li>화살표 함수 -&gt; <code>=&gt;</code>를 사용해 함수를 간단히 정의, 함수 안의 return문은 소괄호로 변환 가능, 한줄일 경우는 이것도 생략 가능</li>
</ul>
<h3 id="12-객체object">12. 객체(Object)</h3>
<pre><code>객체명 = {
    key : value,
    key : value,
    ...
}</code></pre><ul>
<li>접근 - <code>객체명.key</code>, <code>객체명[&#39;key&#39;]</code></li>
<li>추가 - <code>객체명.key = value;</code>, <code>객체명[&#39;key&#39;] = value;</code></li>
<li>삭제 - <code>delete 객체명.key</code></li>
<li>존재 여부 확인 - 존재하지 않는 property의 경우 undefined</li>
<li>for ... in 반복문<pre><code>for (let key in 객체명) {
  console.log(key)
  console.log(객체명[key])
}</code></pre></li>
</ul>
<h3 id="13-객체object---method-this">13. 객체(Object) - method, this</h3>
<ul>
<li>method : 객체 프로퍼티로 할당 된 함수</li>
<li>this =&gt; 객체를 가리킴<ul>
<li>화살표 함수는 일반 함수와 달리 자신만의 this를 가지지 않아 함수 내부에서 this를 사용하면, 외부에서 값을 가져옴.
<img src="https://velog.velcdn.com/images/haeng_9/post/e96bcf08-d3ab-4e12-8354-ff083658356d/image.png" alt="">
<img src="https://velog.velcdn.com/images/haeng_9/post/133fe365-5a77-43e4-a40f-dc95c555af8a/image.png" alt=""></li>
</ul>
</li>
</ul>
<h3 id="14-배열array">14. 배열(Array)</h3>
<p>: 순서가 있는 리스트 <code>[]</code></p>
<ul>
<li>문자, 숫자, 객체, 함수 등 포함 가능</li>
<li><code>.length</code> - 배열의 길이 반환</li>
<li><code>.push()</code> - 배열 끝에 추가</li>
<li><code>.pop()</code> - 배열 끝에 요소 제거</li>
<li><code>.unshift()</code> - 배열 앞에 추가</li>
<li><code>.shift()</code> - 배열 앞에 요소 제거</li>
<li>for문 / for ... of문</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL | 240120] Django - 모델 변경, 글쓴이 표시]]></title>
            <link>https://velog.io/@haeng_9/TIL-240120-Django-%EB%AA%A8%EB%8D%B8-%EB%B3%80%EA%B2%BD-%EA%B8%80%EC%93%B4%EC%9D%B4-%ED%91%9C%EC%8B%9C</link>
            <guid>https://velog.io/@haeng_9/TIL-240120-Django-%EB%AA%A8%EB%8D%B8-%EB%B3%80%EA%B2%BD-%EA%B8%80%EC%93%B4%EC%9D%B4-%ED%91%9C%EC%8B%9C</guid>
            <pubDate>Mon, 22 Jan 2024 07:09:01 GMT</pubDate>
            <description><![CDATA[<h2 id="모델-변경">모델 변경</h2>
<hr>
<h3 id="1-question-속성-추가">1. <a href="https://wikidocs.net/71306#question">Question 속성 추가</a></h3>
<p>: Question에 author(글쓴이) 속성 추가</p>
<p><strong>pybo/models.py</strong></p>
<pre><code class="language-python">from django.db import models
from django.contrib.auth.models import User

class Question(models.Model):
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    (... 생략 ...)</code></pre>
<ul>
<li>User → <code>django.contrib.auth</code> 앱이 제공하는 사용자 모델. 회원 가입시 데이터 저장에 사용한 모델.ForeignKey로 적용해 선언<ul>
<li>author 속성에 null 허용하려면 <code>null=True</code></li>
</ul>
</li>
</ul>
<p>🚨 모델 변경 후 반드시 <code>makemigrations</code>, <code>migrate</code> 를 통해 데이터베이스 변경해야 함.</p>
<h3 id="2-answer-속성-추가">2. <a href="https://wikidocs.net/71306#answer">Answer 속성 추가</a></h3>
<p>: Answer에 author(글쓴이) 속성 추가</p>
<p><strong>pybo/models.py</strong></p>
<pre><code class="language-python">(... 생략 ...)

class Answer(models.Model):
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    (... 생략 ...)</code></pre>
<p>🚨 모델 변경 후 반드시 <code>makemigrations</code>, <code>migrate</code> 를 통해 데이터베이스 변경해야 함.</p>
<h3 id="3-author-저장">3. <a href="https://wikidocs.net/71306#author">author 저장</a></h3>
<p>: 질문과 답변 저장시 author도 함께 저장해야 함. </p>
<p><strong>pybo/views.py</strong></p>
<pre><code class="language-python">def answer_create(request, question_id):
    (... 생략 ...)
        if form.is_valid():
            answer = form.save(commit=False)
            answer.author = request.user  # author 속성에 로그인 계정 저장
            (... 생략 ...)
    (... 생략 ...)

def question_create(request):
    (... 생략 ...)
        if form.is_valid():
            question = form.save(commit=False)
            question.author = request.user  # author 속성에 로그인 계정 저장
    (... 생략 ...)</code></pre>
<ul>
<li><code>request.user</code> → 현재 로그인한 계정의 User 모델 객체</li>
</ul>
<h3 id="4-로그인이-필요한-함수">4. <a href="https://wikidocs.net/71306#_1">로그인이 필요한 함수</a></h3>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/f333082c-9427-4755-842b-9df2b0004abd/image.png" alt=""></p>
<ul>
<li><code>request.user</code>가 User 객체가 아닌 AnonymousUser 객체이므로 에러 발생<ul>
<li>로그아웃 상태이면 AnonymousUser 객체가, 로그인 상태이면 User 객체가 들어있는데, 우리는 author 속성을 정의할 때 User를 이용하도록 함. ⇒  <code>answer.author = request.user</code>에서 User 대신 AnonymousUser가 대입되어 오류가 발생</li>
</ul>
</li>
</ul>
<p>⇒  <code>request.user</code>를 사용하는 함수에 <code>@login_required</code> 애너테이션을 사용 해 로그인 필요한 함수로 만듦</p>
<p><strong>pybo/views.py</strong></p>
<pre><code class="language-python">from django.shortcuts import render, get_object_or_404, redirect
from django.utils import timezone
from .models import Question
from .forms import QuestionForm, AnswerForm
from django.core.paginator import Paginator
from django.contrib.auth.decorators import login_required

(... 생략 ...)

@login_required(login_url=&#39;common:login&#39;)
def answer_create(request, question_id):
    (... 생략 ...)

@login_required(login_url=&#39;common:login&#39;)
def question_create(request):
    (... 생략 ...)</code></pre>
<ul>
<li>answer_create 함수와 question_create 함수 → 로그인이 필요하므로 <code>@login_required</code> 어노테이션 사용</li>
<li>로그아웃 상태에서 <code>@login_required</code> 어노테이션이 적용된 함수가 호출되면 자동으로 로그인 화면으로 이동.</li>
<li><code>@login_required</code> 어노테이션은 <code>login_url=&#39;common:login&#39;</code> 처럼 로그인 URL을 지정 가능</li>
</ul>
<h3 id="5-next">5. <a href="https://wikidocs.net/71306#next">next</a></h3>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/c6d8327e-85c3-447e-b490-9966e64ec268/image.png" alt=""></p>
<ul>
<li>로그인 후 next 파라미터에 있는 URL로 페이지를 이동하려면 로그인 템플릿에  hidden 타입의 next 항목을 추가</li>
</ul>
<p><strong>common/login.html</strong></p>
<pre><code class="language-html">(... 생략 ...)
&lt;form method=&quot;post&quot; action=&quot;{% url &#39;common:login&#39; %}&quot;&gt;
    {% csrf_token %}
    &lt;input type=&quot;hidden&quot; name=&quot;next&quot; value=&quot;{{ next }}&quot;&gt;  &lt;!-- 로그인 성공후 이동되는 URL --&gt;
    {% include &quot;form_errors.html&quot; %}
(... 생략 ...)</code></pre>
<h3 id="6-disabled">6. <a href="https://wikidocs.net/71306#disabled">disabled</a></h3>
<p><strong>pybo/question_detail.html</strong></p>
<pre><code class="language-html">(... 생략 ...)
&lt;div class=&quot;mb-3&quot;&gt;
    &lt;label for=&quot;content&quot; class=&quot;form-label&quot;&gt;답변내용&lt;/label&gt;
    &lt;textarea {% if not user.is_authenticated %}disabled{% endif %}
              name=&quot;content&quot; id=&quot;content&quot; class=&quot;form-control&quot; rows=&quot;10&quot;&gt;&lt;/textarea&gt;
&lt;/div&gt;
&lt;input type=&quot;submit&quot; value=&quot;답변등록&quot; class=&quot;btn btn-primary&quot;&gt;
(... 생략 ...)</code></pre>
<ul>
<li><code>{% if not user.is_authenticated %}</code> (로그인 상태가 아닌 경우) textarea 태그에 <code>disabled</code> 속성을 적용 → 로그아웃 시 답변 작성 불가하도록 지정</li>
</ul>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/39b22b4a-2b9b-46c7-9a9f-e43308fa625c/image.png" alt=""></p>
<p>→ 로그아웃 상태에서 답변 등록 클릭 후 로그인 시 발생하는 에러</p>
<ul>
<li>로그인 시에 전달된 next 파라미터 때문에 로그인 후에 답변등록 URL인 <code>/answer/create</code>가 GET 방식으로 호출. (답변 등록시에는 POST가 아닌 경우 <code>HttpResponseNotAllowed</code> 오류를 발생하도록 설정해서 405 에러 발생)</li>
</ul>
<p>해결 :</p>
<p><strong>pybo/views.py</strong> 수정</p>
<pre><code class="language-html">(... 생략 ...)
from django.http import HttpResponseNotAllowed
(... 생략 ...)

@login_required(login_url=&#39;common:login&#39;)
def answer_create(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    if request.method == &#39;POST&#39;:
        form = AnswerForm(request.POST)
        if form.is_valid():
            answer = form.save(commit=False)
            answer.author = request.user  # author 속성에 로그인 계정 저장
            answer.create_date = timezone.now()
            answer.question = question
            answer.save()
            return redirect(&#39;pybo:detail&#39;, question_id=question.id)
    else:
        form = AnswerForm()
    context = {&#39;question&#39;: question, &#39;form&#39;: form}
    return render(request, &#39;pybo/question_detail.html&#39;, context)

(... 생략 ...)</code></pre>
<h2 id="글쓴이-표시">글쓴이 표시</h2>
<hr>
<h3 id="1-질문-목록">1. <a href="https://wikidocs.net/71307#_1">질문 목록</a></h3>
<p><strong>question_list.html</strong></p>
<pre><code class="language-html">(... 생략 ...)
&lt;tr class=&quot;text-center table-dark&quot;&gt;
    &lt;th&gt;번호&lt;/th&gt;
    &lt;th style=&quot;width:50%&quot;&gt;제목&lt;/th&gt;
    &lt;th&gt;글쓴이&lt;/th&gt;
    &lt;th&gt;작성일시&lt;/th&gt;
&lt;/tr&gt;
(... 생략 ...)

{% for question in question_list %}
&lt;tr class=&quot;text-center&quot;&gt;
    &lt;td&gt;
        &lt;!-- 번호 = 전체건수 - 시작인덱스 - 현재인덱스 + 1 --&gt;
        {{ question_list.paginator.count|sub:question_list.start_index|sub:forloop.counter0|add:1 }}
    &lt;/td&gt;
    &lt;td class=&quot;text-start&quot;&gt;
        &lt;a href=&quot;{% url &#39;pybo:detail&#39; question.id %}&quot;&gt;{{ question.subject }}&lt;/a&gt;
        {% if question.answer_set.count &gt; 0 %}
        &lt;span class=&quot;text-danger small mx-2&quot;&gt;{{ question.answer_set.count }}&lt;/span&gt;
        {% endif %}
    &lt;/td&gt;
    &lt;td&gt;{{ question.author.username }}&lt;/td&gt;  &lt;!-- 글쓴이 추가 --&gt;
    &lt;td&gt;{{ question.create_date }}&lt;/td&gt;
&lt;/tr&gt;
{% endfor %}
{% else %}
&lt;tr&gt;
    &lt;td colspan=&quot;4&quot;&gt;질문이 없습니다.&lt;/td&gt;
&lt;/tr&gt;
(... 생략 ...)</code></pre>
<ul>
<li>th 엘리먼트 가운데 정렬, 제목의 너비가 전체에서 50% 차지하도록 지정</li>
<li><code>&lt;td&gt;{{ question.author.username }}&lt;/td&gt;</code> → 질문 글쓴이 표시</li>
<li><code>&lt;td colspan=&quot;4&quot;&gt;질문이 없습니다.&lt;/td&gt;</code> → 테이블 항목 개수 3에서 4로 증가</li>
</ul>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/b215d9a5-e6f9-4b34-a03d-65bba6d2c8a6/image.png" alt=""></p>
<h3 id="2-질문-상세">2. <a href="https://wikidocs.net/71307#_2">질문 상세</a></h3>
<p><strong>question_detail.html</strong></p>
<pre><code class="language-html">(... 생략 ...)
&lt;!-- 질문 --&gt;
&lt;h2 class=&quot;border-bottom py-2&quot;&gt;{{ question.subject }}&lt;/h2&gt;
&lt;div class=&quot;card-body&quot;&gt;
    &lt;div class=&quot;card-text&quot; style=&quot;white-space: pre-line;&quot;&gt;{{ question.content }}&lt;/div&gt;
    &lt;div class=&quot;d-flex justify-content-end&quot;&gt;
        &lt;div class=&quot;badge bg-light text-dark p-2 text-start&quot;&gt;
            &lt;div class=&quot;mb-2&quot;&gt;{{ question.author.username }}&lt;/div&gt;
            &lt;div&gt;{{ question.create_date }}&lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;

(... 생략 ...)

&lt;!-- 답변 --&gt;
&lt;h5 class=&quot;border-bottom my-3 py-2&quot;&gt;{{question.answer_set.count}}개의 답변이 있습니다.&lt;/h5&gt;
{% for answer in question.answer_set.all %}
&lt;div class=&quot;card-body&quot;&gt;
    &lt;div class=&quot;card-text&quot; style=&quot;white-space: pre-line;&quot;&gt;{{ answer.content }}&lt;/div&gt;
    &lt;div class=&quot;d-flex justify-content-end&quot;&gt;
        &lt;div class=&quot;badge bg-light text-dark p-2 text-start&quot;&gt;
            &lt;div class=&quot;mb-2&quot;&gt;{{ answer.author.username }}&lt;/div&gt;
            &lt;div&gt;{{ answer.create_date }}&lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
(... 생략 ...)</code></pre>
<ul>
<li>질문 목록과 같이 글쓴이 추가</li>
</ul>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/e0769447-984b-4e72-8882-f600c7c7ccc6/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL | 240117] Django - 회원가입]]></title>
            <link>https://velog.io/@haeng_9/TIL-240117-Django-%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85</link>
            <guid>https://velog.io/@haeng_9/TIL-240117-Django-%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85</guid>
            <pubDate>Thu, 18 Jan 2024 03:24:20 GMT</pubDate>
            <description><![CDATA[<h3 id="1-회원가입-링크">1. <a href="https://wikidocs.net/71303#_1">회원가입 링크</a></h3>
<p><strong>navbar.html</strong></p>
<pre><code class="language-html">&lt;nav class=&quot;navbar navbar-expand-lg navbar-light bg-light border-bottom&quot;&gt;
    &lt;div class=&quot;container-fluid&quot;&gt;
        (... 생략 ...)
        &lt;div class=&quot;collapse navbar-collapse&quot; id=&quot;navbarSupportedContent&quot;&gt;
            &lt;ul class=&quot;navbar-nav me-auto mb-2 mb-lg-0&quot;&gt;
                &lt;li class=&quot;nav-item&quot;&gt;
                    {% if user.is_authenticated %}
                    &lt;a class=&quot;nav-link&quot; href=&quot;{% url &#39;common:logout&#39; %}&quot;&gt;{{ user.username }} (로그아웃)&lt;/a&gt;
                    {% else %}
                    &lt;a class=&quot;nav-link&quot; href=&quot;{% url &#39;common:login&#39; %}&quot;&gt;로그인&lt;/a&gt;
                    {% endif %}
                &lt;/li&gt;
                &lt;li&gt;
                    {% if not user.is_authenticated %}
                    &lt;a class=&quot;nav-link&quot; href=&quot;{% url &#39;common:signup&#39; %}&quot;&gt;회원가입&lt;/a&gt;
                    {% endif %}
                &lt;/li&gt;
            &lt;/ul&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/nav&gt;</code></pre>
<p>→ 로그아웃 상태에서만 “회원가입” 링크 보이도록 함</p>
<h3 id="2-urlspy">2. <a href="https://wikidocs.net/71303#urlspy">urls.py</a></h3>
<p><strong>common/urls.py</strong></p>
<pre><code class="language-python">from django.urls import path
from django.contrib.auth import views as auth_views
from . import views

app_name = &#39;common&#39;

urlpatterns = [
    path(&#39;login/&#39;, auth_views.LoginView.as_view(template_name=&#39;common/login.html&#39;), name=&#39;login&#39;),
    path(&#39;logout/&#39;, views.logout_view, name=&#39;logout&#39;),
    path(&#39;signup/&#39;, views.signup, name=&#39;signup&#39;),
]</code></pre>
<ul>
<li>url 매핑 규칙 추가</li>
</ul>
<h3 id="3-formspy">3. <a href="https://wikidocs.net/71303#formspy">forms.py</a></h3>
<ul>
<li>계정 생성시 사용할 UserForm 생성</li>
</ul>
<p><strong>common/forms.py</strong></p>
<pre><code class="language-python">from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User

class UserForm(UserCreationForm):
    email = forms.EmailField(label=&quot;이메일&quot;)

    class Meta:
        model = User
        fields = (&quot;username&quot;, &quot;password1&quot;, &quot;password2&quot;, &quot;email&quot;)</code></pre>
<ul>
<li><p>UserForm을 따로 만들지 않고 UserCreationForm을 그대로 사용해도 되지만,</p>
<p>  이메일 등의 속성을 추가하기 위해서는 UserCreationForm 클래스를 상속해야 함.</p>
</li>
</ul>
<table>
<thead>
<tr>
<th>속성명</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>username</td>
<td>사용자이름</td>
</tr>
<tr>
<td>password1</td>
<td>비밀번호1</td>
</tr>
<tr>
<td>password2</td>
<td>비밀번호2 (비밀번호1을 제대로 입력했는지 대조하기 위한 값)</td>
</tr>
</tbody></table>
<h3 id="4-viewspy">4. <a href="https://wikidocs.net/71303#viewspy">views.py</a></h3>
<p><strong>common/views.py</strong> 수정</p>
<pre><code class="language-python">from django.contrib.auth import authenticate, login, logout
from django.shortcuts import render, redirect
from common.forms import UserForm

def logout_view(request):
    logout(request)
    return redirect(&#39;index&#39;)

def signup(request):
    if request.method == &quot;POST&quot;:
        form = UserForm(request.POST)
        if form.is_valid():
            form.save()
            username = form.cleaned_data.get(&#39;username&#39;)
            raw_password = form.cleaned_data.get(&#39;password1&#39;)
            user = authenticate(username=username, password=raw_password)  # 사용자 인증
            login(request, user)  # 로그인
            return redirect(&#39;index&#39;)
    else:
        form = UserForm()
    return render(request, &#39;common/signup.html&#39;, {&#39;form&#39;: form})</code></pre>
<ul>
<li>POST → 화면에서 입력한 데이터로 사용자를 생성. GET →  회원가입 화면을 보여줌</li>
<li><code>form.cleaned_data.get</code> : 폼의 입력값을 개별적으로 얻고 싶은 경우에 사용 (여기서는 인증시 사용할 사용자명과 비밀번호를 얻기 위해 사용)</li>
<li>authenticate, login 함수 → 신규 사용자생성 후 자동 로그인 될 수 있도록<ul>
<li><code>django.contrib.auth.authenticate</code> - 사용자 인증을 담당(사용자명과 비밀번호가 정확한지 검증)</li>
<li><code>django.contrib.auth.login</code> - 로그인을 담당(사용자 세션을 생성)</li>
</ul>
</li>
</ul>
<h3 id="5-signuphtml">5. <a href="https://wikidocs.net/71303#signuphtml">signup.html</a></h3>
<p><strong>templates/common/signup.html</strong> 생성</p>
<pre><code class="language-html">{% extends &quot;base.html&quot; %}
{% block content %}
&lt;div class=&quot;container my-3&quot;&gt;
    &lt;form method=&quot;post&quot; action=&quot;{% url &#39;common:signup&#39; %}&quot;&gt;
        {% csrf_token %}
        {% include &quot;form_errors.html&quot; %}
        &lt;div class=&quot;mb-3&quot;&gt;
            &lt;label for=&quot;username&quot;&gt;사용자 이름&lt;/label&gt;
            &lt;input type=&quot;text&quot; class=&quot;form-control&quot; name=&quot;username&quot; id=&quot;username&quot;
                   value=&quot;{{ form.username.value|default_if_none:&#39;&#39; }}&quot;&gt;
        &lt;/div&gt;
        &lt;div class=&quot;mb-3&quot;&gt;
            &lt;label for=&quot;password1&quot;&gt;비밀번호&lt;/label&gt;
            &lt;input type=&quot;password&quot; class=&quot;form-control&quot; name=&quot;password1&quot; id=&quot;password1&quot;
                   value=&quot;{{ form.password1.value|default_if_none:&#39;&#39; }}&quot;&gt;
        &lt;/div&gt;
        &lt;div class=&quot;mb-3&quot;&gt;
            &lt;label for=&quot;password2&quot;&gt;비밀번호 확인&lt;/label&gt;
            &lt;input type=&quot;password&quot; class=&quot;form-control&quot; name=&quot;password2&quot; id=&quot;password2&quot;
                   value=&quot;{{ form.password2.value|default_if_none:&#39;&#39; }}&quot;&gt;
        &lt;/div&gt;
        &lt;div class=&quot;mb-3&quot;&gt;
            &lt;label for=&quot;email&quot;&gt;이메일&lt;/label&gt;
            &lt;input type=&quot;text&quot; class=&quot;form-control&quot; name=&quot;email&quot; id=&quot;email&quot;
                   value=&quot;{{ form.email.value|default_if_none:&#39;&#39; }}&quot;&gt;
        &lt;/div&gt;
        &lt;button type=&quot;submit&quot; class=&quot;btn btn-primary&quot;&gt;생성하기&lt;/button&gt;
    &lt;/form&gt;
&lt;/div&gt;
{% endblock %}</code></pre>
<ul>
<li><code>{% include &quot;form_errors.html&quot; %}</code> → 오류 표시</li>
<li>UserForm의 속성인 사용자이름, 비밀번호1, 비밀번호2, 이메일에 해당되는 필드들을 form 항목으로 추가</li>
</ul>
<h3 id="6-회원가입">6. <a href="https://wikidocs.net/71303#_2">회원가입</a></h3>
<p>(1) 메인 페이지 
<img src="https://velog.velcdn.com/images/haeng_9/post/7c37b916-23c7-487e-adac-48652c9a1427/image.png" alt=""></p>
<p>(2) 회원가입 실패 화면 
<img src="https://velog.velcdn.com/images/haeng_9/post/53ba3092-864f-4bd4-a97a-bf8205e716bb/image.png" alt=""></p>
<p>(3) 회원 가입후 <code>/admin</code> 접근
<img src="https://velog.velcdn.com/images/haeng_9/post/5cddfbb9-c854-4f6b-8fe0-b523fb9b394a/image.png" alt=""></p>
<ul>
<li>슈퍼유저가 아닌 계정으로 접근해 경고 메세지 발생</li>
</ul>
<p>(4) 슈퍼유저로 로그인 후 사용자 확인
<img src="https://velog.velcdn.com/images/haeng_9/post/58749a09-86a4-40ff-a8ef-c2cea36b876e/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL | 240116] Django - 로그인, 로그아웃]]></title>
            <link>https://velog.io/@haeng_9/TIL-2401016-Django-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EB%A1%9C%EA%B7%B8%EC%95%84%EC%9B%83</link>
            <guid>https://velog.io/@haeng_9/TIL-2401016-Django-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EB%A1%9C%EA%B7%B8%EC%95%84%EC%9B%83</guid>
            <pubDate>Thu, 18 Jan 2024 02:59:45 GMT</pubDate>
            <description><![CDATA[<ul>
<li>Django에서는 <code>django.contrib.auth</code> 앱을 활용하여 로그인, 로그아웃 구현 가능<ul>
<li>Django 프로젝트 생성 시 자동으로 생성 (<code>settings.py</code> 에서 확인 가능)</li>
</ul>
</li>
</ul>
<h3 id="1-common-앱">1. common 앱</h3>
<ul>
<li>별도의 앱으로 생성</li>
</ul>
<p>(1) <code>django-admin startapp common</code></p>
<p>(2) common 앱 등록</p>
<p><strong>config/settings.py</strong></p>
<pre><code class="language-python">(... 생략 ...)

INSTALLED_APPS = [
    &#39;common.apps.CommonConfig&#39;,  
    &#39;pybo.apps.PyboConfig&#39;,
    &#39;django.contrib.admin&#39;,
    &#39;django.contrib.auth&#39;,
    &#39;django.contrib.contenttypes&#39;,
    &#39;django.contrib.sessions&#39;,
    &#39;django.contrib.messages&#39;,
    &#39;django.contrib.staticfiles&#39;,
]

(... 생략 ...)</code></pre>
<p>(3) url 등록</p>
<p><strong>config/urls.py</strong> 수정</p>
<pre><code class="language-python">from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path(&#39;admin/&#39;, admin.site.urls),
    path(&#39;pybo/&#39;, include(&#39;pybo.urls&#39;)),
    path(&#39;common/&#39;, include(&#39;common.urls&#39;)),
]</code></pre>
<p>→ <code>http://localhost:8000/common/</code> 으로 시작하는 URL은 모두 <code>common/urls.py</code> 파일을 참조</p>
<p><strong>common/urls.py</strong> 생성</p>
<pre><code class="language-python">app_name = &#39;common&#39;

urlpatterns = [
]</code></pre>
<h3 id="2-로그인">2. 로그인</h3>
<p><strong>templates/navbar.html</strong></p>
<pre><code class="language-html">(... 생략 ...)
&lt;ul class=&quot;navbar-nav&quot;&gt;
    &lt;li class=&quot;nav-item &quot;&gt;
        &lt;a class=&quot;nav-link&quot; href=&quot;{% url &#39;common:login&#39; %}&quot;&gt;로그인&lt;/a&gt;
    &lt;/li&gt;
&lt;/ul&gt;
(... 생략 ...)</code></pre>
<p>→ 로그인 버튼 클릭시 화면 이동</p>
<p><strong>1) 로그인 뷰</strong></p>
<p><strong>common/urls.py</strong> 수정</p>
<pre><code class="language-python">from django.urls import path
from django.contrib.auth import views as auth_views

app_name = &#39;common&#39;

urlpatterns = [
    path(&#39;login/&#39;, auth_views.LoginView.as_view(), name=&#39;login&#39;),
]</code></pre>
<p>→ 매핑 규칙 추가. 뷰를 따로 만들지 않고 내장된 LoginView 사용</p>
<p><strong>2) 로그인 템플릿</strong></p>
<p>(1) <strong>common/urls.py</strong> 수정</p>
<pre><code class="language-python">from django.urls import path
from django.contrib.auth import views as auth_views

app_name = &#39;common&#39;

urlpatterns = [
    path(&#39;login/&#39;, auth_views.LoginView.as_view(template_name=&#39;common/login.html&#39;), name=&#39;login&#39;),
]</code></pre>
<ul>
<li>LoginView는 registration이라는 템플릿 디렉터리에서 login.html 파일을 찾지만 common앱에서 구현하므로 경로를 <code>common/login.html</code> 로 수정 및 생성</li>
</ul>
<p>(2) <strong>templates/common/login.html</strong> 생성</p>
<pre><code class="language-html">{% extends &quot;base.html&quot; %}
{% block content %}
&lt;div class=&quot;container my-3&quot;&gt;
    &lt;form method=&quot;post&quot; action=&quot;{% url &#39;common:login&#39; %}&quot;&gt;
        {% csrf_token %}
        {% include &quot;form_errors.html&quot; %}
        &lt;div class=&quot;mb-3&quot;&gt;
            &lt;label for=&quot;username&quot;&gt;사용자ID&lt;/label&gt;
            &lt;input type=&quot;text&quot; class=&quot;form-control&quot; name=&quot;username&quot; id=&quot;username&quot;
                   value=&quot;{{ form.username.value|default_if_none:&#39;&#39; }}&quot;&gt;
        &lt;/div&gt;
        &lt;div class=&quot;mb-3&quot;&gt;
            &lt;label for=&quot;password&quot;&gt;비밀번호&lt;/label&gt;
            &lt;input type=&quot;password&quot; class=&quot;form-control&quot; name=&quot;password&quot; id=&quot;password&quot;
                   value=&quot;{{ form.password.value|default_if_none:&#39;&#39; }}&quot;&gt;
        &lt;/div&gt;
        &lt;button type=&quot;submit&quot; class=&quot;btn btn-primary&quot;&gt;로그인&lt;/button&gt;
    &lt;/form&gt;
&lt;/div&gt;
{% endblock %}</code></pre>
<ul>
<li><code>username</code> , <code>password</code> 는 <code>django.contrib.auth</code> 앱이 요구하는 필수항목</li>
</ul>
<p>(3) <strong>templates/form_errors.html</strong> 작성</p>
<pre><code class="language-html">&lt;!-- 필드 오류와 넌필드 오류를 출력한다. --&gt;
{% if form.errors %}
&lt;div class=&quot;alert alert-danger&quot;&gt;
    {% for field in form %}
    &lt;!-- 필드 오류 --&gt;
    {% if field.errors %}
    &lt;div&gt;
        &lt;strong&gt;{{ field.label }}&lt;/strong&gt;
        {{ field.errors }}
    &lt;/div&gt;
    {% endif %}
    {% endfor %}
    &lt;!-- 넌필드 오류 --&gt;
    {% for error in form.non_field_errors %}
    &lt;div&gt;
        &lt;strong&gt;{{ error }}&lt;/strong&gt;
    &lt;/div&gt;
    {% endfor %}
&lt;/div&gt;
{% endif %}</code></pre>
<ul>
<li>로그인 실패시 오류 사항 확인 페이지<ul>
<li><code>field.errors</code> : 사용자 입력 필드 값에 대한 오류(값 누락, 필드 형식 불일치 등)</li>
<li><code>form.non_field_errors</code>  : 필드 값 상관없이 다른 이유로 발생하는 오류</li>
</ul>
</li>
</ul>
<p><strong>3) 로그인 수행</strong></p>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/f60108fd-bd11-4d4b-bcd4-0b988c4c9572/image.png" alt=""></p>
<p>→ 오류 메시지 표시</p>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/4e454ec4-f7dc-4460-9579-05ff3b45352a/image.png" alt=""></p>
<p>→ 슈퍼유저 계정으로 로그인</p>
<p>(1) <strong>config/settings.py</strong> 수정</p>
<pre><code class="language-python">(... 생략 ...)

# 로그인 성공후 이동하는 URL
LOGIN_REDIRECT_URL = &#39;/&#39;</code></pre>
<ul>
<li><code>django.contrib.auth</code> 패키지는 디폴트로 <code>/accounts/profile/</code> 이라는 URL로 이동하지만 성공시 <code>/</code> 로 이동하도록 변경</li>
</ul>
<p>(2) <strong>config/urls.py</strong> 수정</p>
<pre><code class="language-python">from django.contrib import admin
from django.urls import path, include
from pybo import views

urlpatterns = [
    path(&#39;admin/&#39;, admin.site.urls),
    path(&#39;pybo/&#39;, include(&#39;pybo.urls&#39;)),
    path(&#39;common/&#39;, include(&#39;common.urls&#39;)),
    path(&#39;&#39;, views.index, name=&#39;index&#39;),  # &#39;/&#39; 에 해당되는 path
]</code></pre>
<ul>
<li><code>/</code>  페이지에 대응하는 URL 매핑 규칙 추가</li>
</ul>
<h3 id="3-로그아웃">3. 로그아웃</h3>
<ul>
<li>로그인 후 “로그인” 링크가 “로그아웃” 링크로 바뀌고, “로그아웃” 링크일 때 “로그인” 링크로 바뀌도록</li>
</ul>
<p>(1) <strong>templates/navbar.html</strong> 수정</p>
<pre><code class="language-html">(... 생략 ...)
&lt;li class=&quot;nav-item&quot;&gt;
    {% if user.is_authenticated %}
    &lt;a class=&quot;nav-link&quot; href=&quot;{% url &#39;common:logout&#39; %}&quot;&gt;{{ user.username }} (로그아웃)&lt;/a&gt;
    {% else %}
    &lt;a class=&quot;nav-link&quot; href=&quot;{% url &#39;common:login&#39; %}&quot;&gt;로그인&lt;/a&gt;
    {% endif %}
&lt;/li&gt;
(... 생략 ...)</code></pre>
<ul>
<li><code>{% if user.is_authenticated %}</code> → 현재 사용자가 로그인 되었는지 판별</li>
</ul>
<p>(2) <strong>common/urls.py</strong></p>
<pre><code class="language-html">from django.urls import path
from django.contrib.auth import views as auth_views
from . import views

app_name = &#39;common&#39;

urlpatterns = [
    path(&#39;login/&#39;, auth_views.LoginView.as_view(template_name=&#39;common/login.html&#39;), name=&#39;login&#39;),
    path(&#39;logout/&#39;, views.logout_view, name=&#39;logout&#39;),
]</code></pre>
<ul>
<li>URL 매핑 추가</li>
</ul>
<p>(3) <strong>common/views.py</strong></p>
<pre><code class="language-python">from django.contrib.auth import logout
from django.shortcuts import redirect

def logout_view(request):
    logout(request)
    return redirect(&#39;index&#39;)</code></pre>
<ul>
<li>logout_view 함수 호출시 <code>django.contrib.auth</code> 모듈의 로그아웃 함수를 사용해 로그아웃 수행 후 ‘index’ 페이지로 리다이렉트하도록 설정</li>
</ul>
<p>[ 로그인 완료 후 화면 ]
<img src="https://velog.velcdn.com/images/haeng_9/post/54667952-c54b-48c1-b60d-d3a8c1c22437/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL | 240111] Django - 템플릿 필터(filter) + 답변 개수 표시]]></title>
            <link>https://velog.io/@haeng_9/TIL-2401011-Django-%ED%85%9C%ED%94%8C%EB%A6%BF-%ED%95%84%ED%84%B0filter</link>
            <guid>https://velog.io/@haeng_9/TIL-2401011-Django-%ED%85%9C%ED%94%8C%EB%A6%BF-%ED%95%84%ED%84%B0filter</guid>
            <pubDate>Thu, 11 Jan 2024 01:01:43 GMT</pubDate>
            <description><![CDATA[<h2 id="템플릿-필터-template-filter">템플릿 필터 (Template filter)</h2>
<hr>
<ul>
<li>템플릿 태그에서 <code>|</code> 문자 뒤에 사용하는 필터</li>
</ul>
<h3 id="1-게시물-번호-오류">1. 게시물 번호 오류</h3>
<ul>
<li>현재 사이트는 어떤 페이지를 들어가도 게시물 번호가 1부터 시작되는 문제 발생</li>
</ul>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/ecd07669-83d0-48ab-a72b-61a780093a3d/image.png" alt=""></p>
<h3 id="2-게시물-번호">2. 게시물 번호</h3>
<ul>
<li><p>페이지별로 게시물의 번호를 역순으로 정렬하기 위한 공식</p>
<p>  <code>번호 = 전체건수 - 시작인덱스 - 현재인덱스 + 1</code> </p>
</li>
<li><p>django에는 빼기 필터가 없으므로 직접 만들어야 함.</p>
<ul>
<li><code>|add:-3</code> 과 같은 경우 add 필터는 인수로 숫자만 받을 수 있어 변수 적용이 안됨.</li>
</ul>
</li>
</ul>
<h3 id="3-템플릿-필터-작성하기">3. 템플릿 필터 작성하기</h3>
<p>1) <code>pybo/templatetags</code> directory 생성 (반드시 templatetags 디렉터리는 앱 디렉터리 하위에 생성)</p>
<p>2) <code>pybo/templatetags/pybo_fileter.py</code> </p>
<pre><code class="language-python">from django import template

register = template.Library()

@register.filter
def sub(value, arg):
    return value - arg </code></pre>
<ul>
<li><code>@register.filter</code> 에너테이션을 적용해 템플릿에서 해당 함수를 필터로 사용</li>
</ul>
<h3 id="4-템플릿-필터-사용하기">4. 템플릿 필터 사용하기</h3>
<p>1) 템플릿 상단에 필터 파일 로드</p>
<blockquote>
<p>템플릿 상단에 extends문이 있으면 load문은 extends문 아래에 위치</p>
</blockquote>
<p>2) <code>question_list.html</code> 수정</p>
<pre><code class="language-html">{% extends &#39;base.html&#39; %}
{% load pybo_filter %}
{% block content %}
&lt;div class=&quot;container my-3&quot;&gt;
    &lt;table class=&quot;table&quot;&gt;
        &lt;thead&gt;
        &lt;tr class=&quot;table-dark&quot;&gt;
            &lt;th&gt;번호&lt;/th&gt;
            &lt;th&gt;제목&lt;/th&gt;
            &lt;th&gt;작성일시&lt;/th&gt;
        &lt;/tr&gt;
        &lt;/thead&gt;
        &lt;tbody&gt;
        {% if question_list %}
        {% for question in question_list %}
        &lt;tr&gt;
            &lt;td&gt;
                &lt;!-- 번호 = 전체건수 - 시작인덱스 - 현재인덱스 + 1 --&gt;
                {{ question_list.paginator.count|sub:question_list.start_index|sub:forloop.counter0|add:1 }}
            &lt;/td&gt;
            &lt;td&gt;
                &lt;a href=&quot;{% url &#39;pybo:detail&#39; question.id %}&quot;&gt;{{ question.subject }}&lt;/a&gt;
            &lt;/td&gt;
            &lt;td&gt;{{ question.create_date }}&lt;/td&gt;
        &lt;/tr&gt;
(... 생략 ...)</code></pre>
<table>
<thead>
<tr>
<th>공식</th>
<th>코드</th>
</tr>
</thead>
<tbody><tr>
<td>전체건수</td>
<td>question_list.paginator.count</td>
</tr>
<tr>
<td>시작인덱스</td>
<td>question_list.start_index</td>
</tr>
<tr>
<td>현재인덱스</td>
<td>forloop.counter0</td>
</tr>
</tbody></table>
<p><strong>[ 결과 ]</strong>
<img src="https://velog.velcdn.com/images/haeng_9/post/2712d09e-1514-412e-8e59-34693d69c23a/image.png" alt=""></p>
<h2 id="답변-개수-표시">답변 개수 표시</h2>
<hr>
<h3 id="-답변-개수-표시">+ 답변 개수 표시</h3>
<ul>
<li>질문 목록에 해당 질문에 달린 답변 개수 표시</li>
</ul>
<p><strong>question.html</strong> 수정</p>
<pre><code class="language-html">(... 생략 ...)
&lt;td&gt;
    &lt;a href=&quot;{% url &#39;pybo:detail&#39; question.id %}&quot;&gt;{{ question.subject }}&lt;/a&gt;
    {% if question.answer_set.count &gt; 0 %}
    &lt;span class=&quot;text-danger small mx-2&quot;&gt;{{ question.answer_set.count }}&lt;/span&gt;
    {% endif %}
&lt;/td&gt;
&lt;...&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/61c13a28-5bca-487f-991e-c831b5b90ae3/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL | 240109] Django - 페이징(Paging)]]></title>
            <link>https://velog.io/@haeng_9/TIL-2401009-Django-%ED%8E%98%EC%9D%B4%EC%A7%95Paging</link>
            <guid>https://velog.io/@haeng_9/TIL-2401009-Django-%ED%8E%98%EC%9D%B4%EC%A7%95Paging</guid>
            <pubDate>Thu, 11 Jan 2024 00:22:55 GMT</pubDate>
            <description><![CDATA[<h2 id="페이징-paging">페이징 (paging)</h2>
<hr>
<h3 id="1-대량-테스트-데이터-생성">1. 대량 테스트 데이터 생성</h3>
<ul>
<li>장고 셸 활용해 데이터(게시물) 대량 생생</li>
</ul>
<p><code>&gt; python [manage.py](http://manage.py) shell</code></p>
<pre><code class="language-python">from django.utils import timezone
from pybo.models import Question

for i in range(300):
    q = Question(subject=&#39;테스트 데이터입니다 : [%03d]&#39; %i,content=&#39;내용무&#39;, create_date=timezone.now())
    q.save()</code></pre>
<ul>
<li>300개 이상의 데이터가 한 페이지에 보여짐</li>
</ul>
<h3 id="2-paginator">2. Paginator</h3>
<ul>
<li>페이징을 위해 사용하는 클래스</li>
</ul>
<p><strong>pybo/views.py</strong></p>
<pre><code class="language-python">from django.shortcuts import render, get_object_or_404, redirect
from django.utils import timezone
from .models import Question
from .forms import QuestionForm, AnswerForm
from django.core.paginator import Paginator  

def index(request):
    page = request.GET.get(&#39;page&#39;, &#39;1&#39;)  # 페이지
    question_list = Question.objects.order_by(&#39;-create_date&#39;)
    paginator = Paginator(question_list, 10)  # 페이지당 10개씩 보여주기
    page_obj = paginator.get_page(page)
    context = {&#39;question_list&#39;: page_obj}
    return render(request, &#39;pybo/question_list.html&#39;, context)

(... 생략 ...)</code></pre>
<ul>
<li><code>page = request.GET.get(&#39;page&#39;, &#39;1&#39;)</code> - <code>http://localhost:8000/pybo/?page=1</code> 처럼 GET 방식으로 호출된 URL에서 page값을 가져올 때 사용.  <code>http://localhost:8000/pybo/</code> 처럼 page값 없이 호출된 경우 디폴트값 1</li>
<li><code>paginator = Paginator(question_list, 10)</code> - (게시물 전체, 페이지 당 보여줄 게시물 개수)</li>
<li><code>page_obj = paginator.get_page(page)</code> - 페이지에 해당되는 페이징 객체</li>
<li>페이징 객체 속성</li>
</ul>
<pre><code>| 항목 | 설명 |
| --- | --- |
| paginator.count | 전체 게시물 개수 |
| paginator.per_page | 페이지당 보여줄 게시물 개수 |
| paginator.page_range | 페이지 범위 |
| number | 현재 페이지 번호 |
| previous_page_number | 이전 페이지 번호 |
| next_page_number | 다음 페이지 번호 |
| has_previous | 이전 페이지 유무 |
| has_next | 다음 페이지 유무 |
| start_index | 현재 페이지 시작 인덱스(1부터 시작) |
| end_index | 현재 페이지의 끝 인덱스(1부터 시작) |</code></pre><h3 id="3-템플릿에-페이징-적용하기--페이지-리스트">3. 템플릿에 페이징 적용하기 &amp; 페이지 리스트</h3>
<ul>
<li><code>question_list.html</code> 에 페이징 객체(question_list)가 전달됨</li>
</ul>
<p><strong>question_list.html</strong> <strong>페이징 처리</strong></p>
<pre><code class="language-html">(... 생략 ...)
    &lt;/table&gt;
    &lt;!-- 페이징처리 시작 --&gt;
    &lt;ul class=&quot;pagination justify-content-center&quot;&gt;
        &lt;!-- 이전페이지 --&gt;
        {% if question_list.has_previous %}
        &lt;li class=&quot;page-item&quot;&gt;
            &lt;a class=&quot;page-link&quot; href=&quot;?page={{ question_list.previous_page_number }}&quot;&gt;이전&lt;/a&gt;
        &lt;/li&gt;
        {% else %}
        &lt;li class=&quot;page-item disabled&quot;&gt;
            &lt;a class=&quot;page-link&quot; tabindex=&quot;-1&quot; aria-disabled=&quot;true&quot; href=&quot;#&quot;&gt;이전&lt;/a&gt;
        &lt;/li&gt;
        {% endif %}
        &lt;!-- 페이지리스트 --&gt;
        {% for page_number in question_list.paginator.page_range %}
                {% if page_number &gt;= question_list.number|add:-5 and page_number &lt;= question_list.number|add:5 %}
        {% if page_number == question_list.number %}
        &lt;li class=&quot;page-item active&quot; aria-current=&quot;page&quot;&gt;
            &lt;a class=&quot;page-link&quot; href=&quot;?page={{ page_number }}&quot;&gt;{{ page_number }}&lt;/a&gt;
        &lt;/li&gt;
        {% else %}
        &lt;li class=&quot;page-item&quot;&gt;
            &lt;a class=&quot;page-link&quot; href=&quot;?page={{ page_number }}&quot;&gt;{{ page_number }}&lt;/a&gt;
        &lt;/li&gt;
        {% endif %}
                {% endif %}
        {% endfor %}
        &lt;!-- 다음페이지 --&gt;
        {% if question_list.has_next %}
        &lt;li class=&quot;page-item&quot;&gt;
            &lt;a class=&quot;page-link&quot; href=&quot;?page={{ question_list.next_page_number }}&quot;&gt;다음&lt;/a&gt;
        &lt;/li&gt;
        {% else %}
        &lt;li class=&quot;page-item disabled&quot;&gt;
            &lt;a class=&quot;page-link&quot; tabindex=&quot;-1&quot; aria-disabled=&quot;true&quot; href=&quot;#&quot;&gt;다음&lt;/a&gt;
        &lt;/li&gt;
        {% endif %}
    &lt;/ul&gt;
    &lt;!-- 페이징처리 끝 --&gt;
    &lt;a href=&quot;{% url &#39;pybo:question_create&#39; %}&quot; class=&quot;btn btn-primary&quot;&gt;질문 등록하기&lt;/a&gt;
&lt;/div&gt;
{% endblock %}</code></pre>
<ul>
<li>이전 페이지가 있는 경우 “이전”링크 활성화, 없으면 비활성화</li>
</ul>
<pre><code>| 페이징 기능 | 코드 |
| --- | --- |
| 이전 페이지가 있는지 체크 | {% if question_list.has_previous %} |
| 이전 페이지 번호 | {{ question_list.previous_page_number }} |
| 다음 페이지가 있는지 체크 | {% if question_list.has_next %} |
| 다음 페이지 번호 | {{ question_list.next_page_number }} |
| 페이지 리스트 루프 | {% for page_number in question_list.paginator.page_range %} |
| 현재 페이지와 같은지 체크 | {% if page_number == question_list.number %} |</code></pre><ul>
<li>부트스트랩의 pagination 컴포넌트 이용</li>
<li><code>{% if page_number &gt;= question_list.number|add:-5 and page_number &lt;= question_list.number|add:5 %}</code> -   <code>question_list.number</code> (현재 페이지) 보다 5만큼 크거나 작은 값만 표시</li>
</ul>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/61dfff7e-23a2-4de2-895f-a5a82056854f/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL | 240108] Django - 내비게이션바]]></title>
            <link>https://velog.io/@haeng_9/TIL-240108-Django-%EB%82%B4%EB%B9%84%EA%B2%8C%EC%9D%B4%EC%85%98%EB%B0%94</link>
            <guid>https://velog.io/@haeng_9/TIL-240108-Django-%EB%82%B4%EB%B9%84%EA%B2%8C%EC%9D%B4%EC%85%98%EB%B0%94</guid>
            <pubDate>Mon, 08 Jan 2024 06:00:49 GMT</pubDate>
            <description><![CDATA[<h2 id="내비게이션바">내비게이션바</h2>
<hr>
<ul>
<li>모든 화면 위쪽에 고정되어 있는 부트스트랩 컴포넌트</li>
<li>부트스트랩 반응형 웹 기능을 통해 브라우저의 크기가 작아지면 내비게이션 바의 링크들은 햄버거 메뉴 버튼이 생성됨.</li>
</ul>
<h3 id="1-내비게이션바">1. 내비게이션바</h3>
<p>1) 모든 페이지에서 보여야 하므로 base.html에 추가</p>
<p><strong>base.html</strong></p>
<pre><code class="language-html">{% load static %}
&lt;!doctype html&gt;
&lt;html lang=&quot;ko&quot;&gt;
&lt;head&gt;
    &lt;!-- Required meta tags --&gt;
    &lt;meta charset=&quot;utf-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1, shrink-to-fit=no&quot;&gt;
    &lt;!-- Bootstrap CSS --&gt;
    &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;{% static &#39;bootstrap.min.css&#39; %}&quot;&gt;
    &lt;!-- pybo CSS --&gt;
    &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;{% static &#39;pybo/style.css&#39; %}&quot;&gt;
    &lt;title&gt;Hello, pybo!&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;!-- 네비게이션바 --&gt;
&lt;nav class=&quot;navbar navbar-expand-lg navbar-light bg-light border-bottom&quot;&gt;
    &lt;div class=&quot;container-fluid&quot;&gt;
        &lt;a class=&quot;navbar-brand&quot; href=&quot;{% url &#39;pybo:index&#39; %}&quot;&gt;Pybo&lt;/a&gt;
        &lt;button class=&quot;navbar-toggler&quot; type=&quot;button&quot;
                data-bs-toggle=&quot;collapse&quot;
                data-bs-target=&quot;#navbarSupportedContent&quot;
                aria-controls=&quot;navbarSupportedContent&quot;
                aria-expanded=&quot;false&quot;
                aria-label=&quot;Toggle navigation&quot;&gt;
            &lt;span class=&quot;navbar-toggler-icon&quot;&gt;&lt;/span&gt;
        &lt;/button&gt;
        &lt;div class=&quot;collapse navbar-collapse&quot; id=&quot;navbarSupportedContent&quot;&gt;
            &lt;ul class=&quot;navbar-nav me-auto mb-2 mb-lg-0&quot;&gt;
                &lt;li class=&quot;nav-item&quot;&gt;
                    &lt;a class=&quot;nav-link&quot; href=&quot;#&quot;&gt;로그인&lt;/a&gt;
                &lt;/li&gt;
            &lt;/ul&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/nav&gt;
&lt;!-- 기본 템플릿 안에 삽입될 내용 Start --&gt;
{% block content %}
{% endblock %}
&lt;!-- 기본 템플릿 안에 삽입될 내용 End --&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/68868563-90c6-4c37-a27f-a14b80c22f81/image.png" alt=""></p>
<p>2) 부트스트랩 자바스크립트 파일 추가</p>
<ul>
<li><p>부트스트랩 파일 중 <code>bootstrap-5.1.3-dist\js\bootstrap.min.js</code> 파일을 static 폴더로 복사</p>
</li>
<li><p>사용할 수 있도록 base.html의 <code>&lt;/body&gt;</code> 태그 바로 위에 추가</p>
<pre><code class="language-html">  (... 생략 ...)
  &lt;!-- 기본 템플릿 안에 삽입될 내용 Start --&gt;
  {% block content %}
  {% endblock %}
  &lt;!-- 기본 템플릿 안에 삽입될 내용 End --&gt;
  &lt;!-- Bootstrap JS --&gt;
  &lt;script src=&quot;{% static &#39;bootstrap.min.js&#39; %}&quot;&gt;&lt;/script&gt;
  &lt;/body&gt;
  &lt;/html&gt;</code></pre>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/9f2d46d7-31e4-4d8b-a659-ccae5afc8e4a/image.png" alt=""></p>
<h3 id="2-include">2. Include</h3>
<ul>
<li>템플릿의 특정 위치에 다른 템플릿을 삽입할 수 있는 태그</li>
<li>보통 템플릿에서 특정 영역이 반복적으로 사용될 경우 중복을 없애기 위해 사용</li>
</ul>
<p>1) <strong>templates/navbar.html</strong> 생성</p>
<pre><code class="language-html">&lt;!-- 네비게이션바 --&gt;
&lt;nav class=&quot;navbar navbar-expand-lg navbar-light bg-light border-bottom&quot;&gt;
    &lt;div class=&quot;container-fluid&quot;&gt;
        &lt;a class=&quot;navbar-brand&quot; href=&quot;{% url &#39;pybo:index&#39; %}&quot;&gt;Pybo&lt;/a&gt;
        &lt;button class=&quot;navbar-toggler&quot; type=&quot;button&quot;
                data-bs-toggle=&quot;collapse&quot;
                data-bs-target=&quot;#navbarSupportedContent&quot;
                aria-controls=&quot;navbarSupportedContent&quot;
                aria-expanded=&quot;false&quot;
                aria-label=&quot;Toggle navigation&quot;&gt;
            &lt;span class=&quot;navbar-toggler-icon&quot;&gt;&lt;/span&gt;
        &lt;/button&gt;
        &lt;div class=&quot;collapse navbar-collapse&quot; id=&quot;navbarSupportedContent&quot;&gt;
            &lt;ul class=&quot;navbar-nav me-auto mb-2 mb-lg-0&quot;&gt;
                &lt;li class=&quot;nav-item&quot;&gt;
                    &lt;a class=&quot;nav-link&quot; href=&quot;#&quot;&gt;로그인&lt;/a&gt;
                &lt;/li&gt;
            &lt;/ul&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/nav&gt;</code></pre>
<p>2) <strong>base.html</strong> 수정</p>
<pre><code class="language-html">&lt;!-- 네비게이션바 --&gt;
{% include &quot;navbar.html&quot; %}</code></pre>
<ul>
<li>앞서 base.html 작성한 네비게이션바 부분을 한 줄로 삽입할 수 있음 (유지 보수에 용이)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL | 240107] Django - 폼(Form)]]></title>
            <link>https://velog.io/@haeng_9/TIL-2401067-Django-%ED%8F%BCForm</link>
            <guid>https://velog.io/@haeng_9/TIL-2401067-Django-%ED%8F%BCForm</guid>
            <pubDate>Sun, 07 Jan 2024 10:46:32 GMT</pubDate>
            <description><![CDATA[<h2 id="폼-form">폼 (Form)</h2>
<hr>
<h3 id="1-질문-등록">1. 질문 등록</h3>
<p>1) 버튼 생성</p>
<p><strong>question_list.html</strong></p>
<pre><code class="language-html">(... 생략 ...)
    &lt;/table&gt;
    &lt;a href=&quot;{% url &#39;pybo:question_create&#39; %}&quot; class=&quot;btn btn-primary&quot;&gt;질문 등록하기&lt;/a&gt;
&lt;/div&gt;
{% endblock %}</code></pre>
<p>2) URL 매핑</p>
<p><strong>pybo/urls.py</strong></p>
<pre><code class="language-python">(... 생략 ...)
urlpatterns = [
    (... 생략 ...)
    path(&#39;question/create/&#39;, views.question_create, name=&#39;question_create&#39;),
]</code></pre>
<p>3) 폼(Form)</p>
<ul>
<li>페이지 요청시 전달되는 파라미터들을 쉽게 관리하기 위해 사용하는 클래스</li>
<li>필수 파라미터의 값이 누락되지 않았는지, 파라미터의 형식은 적절한지 등을 검증할 목적으로 사용</li>
<li>HTML을 자동으로 생성하거나 폼에 연결된 모델을 이용하여 데이터를 저장하는 기능도 있음</li>
</ul>
<p><strong>pybo/forms.py</strong></p>
<pre><code class="language-python">from django import forms 
from pybo.models import Question

class QuestionForm(forms.ModelForm):
    class Meta:
        model = Question #사용할 모델
        fields = [&#39;subject&#39;, &#39;content&#39;] # QuestionForm에서 사용할 Question 모델의 속성</code></pre>
<ul>
<li>모델 폼(<code>forms.ModelForm</code>) : 모델(Model)과 연결된 폼으로 폼을 저장하면 연결된 모델의 데이터를 저장할수 있는 폼</li>
<li><code>Meta</code> 클래스 : 모델 폼 사용시 필수. 사용할 모델과 모델의 속성 작성</li>
</ul>
<p>4) 뷰 함수</p>
<p><strong>pybo/views.py</strong></p>
<pre><code class="language-python">from django.shortcuts import render, get_object_or_404, redirect
from django.utils import timezone
from .models import Question
from .forms import QuestionForm

(... 생략 ...)

def question_create(request):
    form = QuestionForm()
    return render(request, &#39;pybo/question_form.html&#39;, {&#39;form&#39;: form})</code></pre>
<p>5) 템플릿</p>
<p><strong>templates/pybo/question_form.html</strong></p>
<pre><code class="language-html">{% extends &#39;base.html&#39; %}
{% block content %}
&lt;div class=&quot;container&quot;&gt;
    &lt;h5 class=&quot;my-3 border-bottom pb-2&quot;&gt;질문등록&lt;/h5&gt;
    &lt;form method=&quot;post&quot;&gt;
        {% csrf_token %}
        {{ form.as_p }}
        &lt;button type=&quot;submit&quot; class=&quot;btn btn-primary&quot;&gt;저장하기&lt;/button&gt;
    &lt;/form&gt;
&lt;/div&gt;
{% endblock %}</code></pre>
<ul>
<li><p><code>{{ form.as_p }}</code> : 폼에 정의한 subject, content 속성에 해당하는 HTML 코드를 자동으로 생성. form은 question_create 함수에서 전달한 QuestionForm의 객체</p>
</li>
<li><p>form 태그에 action 속성을 지정하지 않으면 현재 페이지의 URL이 디폴트 action으로 설정</p>
<pre><code>⇒ 동일한 템플릿을 여러 기능에서 함께 사용할 경우, form의 action 속성을 비워두는 트릭을 종종 사용</code></pre></li>
</ul>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/8931d6c6-08fc-47af-9047-9685ccff4a03/image.png" alt=""></p>
<p>6) 답변 저장 ( GET, POST)</p>
<p><strong>pybo/views.py</strong> 수정</p>
<pre><code class="language-python">def question_create(request):
    if request.method == &#39;POST&#39;: # 저장하기 
        form = QuestionForm(request.POST)
        if form.is_valid(): # 폼이 유효하면
            question = form.save(commit=False) # 임시 저장해 question 객체 리턴
            question.create_date = timezone.now() # 실제 저장을 위해 작성일시 설정
            question.save() # 데이터를 실제로 저장
            return redirect(&#39;pybo:index&#39;)
    else: # 질문 등록하기
        form = QuestionForm()
    context = {&#39;form&#39;:form}
    return render(request, &#39;pybo/question_form.html&#39;, context)</code></pre>
<ul>
<li><p>&quot;질문 등록하기&quot; 버튼을 클릭한 경우에는 <code>/pybo/question/create/</code> 페이지가 GET 방식으로 요청되어 question_create 함수가 실행</p>
<p>  ⇒ <code>href</code> 와 같이 링크 페이지를 통해 요청할 경우 무조건 <strong>GET</strong> 방식 사용</p>
</li>
<li><p><code>request.POST</code>에는 화면에서 사용자가 입력한 내용들이 담겨있음</p>
</li>
<li><p><code>request.POST</code>를 인수로 QuestionForm을 생성할 경우 → <code>request.POST</code>에 담긴 subject, content 값이 QuestionForm의 subject, content 속성에 자동으로 저장되어 객체가 생성</p>
</li>
<li><p><code>question = form.save(commit=False)</code> → form에 저장된 데이터로 Question 데이터를 저장하기 위한 코드</p>
</li>
<li><p><code>commit=False</code> → 임시 저장 ( DB 저장 X)</p>
</li>
</ul>
<p>7) 폼 위젯</p>
<p><strong><a href="http://forms.py">forms.py</a></strong> 수정</p>
<pre><code class="language-python">from django import forms 
from pybo.models import Question

class QuestionForm(forms.ModelForm):
    class Meta:
        model = Question #사용할 모델
        fields = [&#39;subject&#39;, &#39;content&#39;] # QuestionForm에서 사용할 Question 모델의 속성
        widgets = {
            &#39;subject&#39; : forms.TextInput(attrs={&#39;class&#39;: &#39;form-control&#39;}),
            &#39;content&#39; : forms.Textarea(attrs={&#39;class&#39;: &#39;form-control&#39;, &#39;rows&#39;:10}),
        }</code></pre>
<ul>
<li><code>{{ form.as_p }}</code> → HTML 코드를 자동으로 생성해 부트스트랩을 적용할 수 없음</li>
<li>widgets 속성을 지정 → subject, content 입력 필드에 <code>form-control</code>과 같은 부트스트랩 클래스를 추가할 수 있음</li>
</ul>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/99e17fbc-75e9-4520-9539-bb5b789d6982/image.png" alt=""></p>
<p>8) 폼 레이블</p>
<p><strong>forms.py</strong></p>
<pre><code class="language-python">from django import forms
from pybo.models import Question

class QuestionForm(forms.ModelForm):
    class Meta:
        model = Question
        fields = [&#39;subject&#39;, &#39;content&#39;]
        widgets = {
            &#39;subject&#39;: forms.TextInput(attrs={&#39;class&#39;: &#39;form-control&#39;}),
            &#39;content&#39;: forms.Textarea(attrs={&#39;class&#39;: &#39;form-control&#39;, &#39;rows&#39;: 10}),
        }
        labels = {
            &#39;subject&#39;: &#39;제목&#39;,
            &#39;content&#39;: &#39;내용&#39;,
        }</code></pre>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/12ae7df1-4dcb-486e-ace7-47d905360c7e/image.png" alt=""></p>
<p>9) 수동 폼 작성</p>
<ul>
<li><code>{{ form.as_p }}</code> → HTML을 빠르게 생성할 수는 있지만 디자인 측면에서 제한이 많음.</li>
</ul>
<p><strong>question_form.html</strong></p>
<pre><code class="language-html">{% extends &#39;base.html&#39; %}

{% block content %}
&lt;div class=&quot;container&quot;&gt;
    &lt;h5 class=&quot;my-3 border-bottom pb-2&quot;&gt;질문등록&lt;/h5&gt;
    &lt;form method=&quot;post&quot;&gt;
        {% csrf_token %}
        &lt;!-- 오류표시 Start --&gt;
        {% if form.errors %}
        &lt;div class=&quot;alert alert-danger&quot; role=&quot;alert&quot;&gt;
            {% for field in form %}
            {% if field.errors %}
            &lt;div&gt;
                &lt;strong&gt;{{ field.label }}&lt;/strong&gt;
                {{ field.errors }}
            &lt;/div&gt;
            {% endif %}
            {% endfor %}
        &lt;/div&gt;
        {% endif %}
        &lt;!-- 오류표시 End --&gt;
        &lt;div class=&quot;mb-3&quot;&gt;
            &lt;label for=&quot;subject&quot; class=&quot;form-label&quot;&gt;제목&lt;/label&gt;
            &lt;input type=&quot;text&quot; class=&quot;form-control&quot; name=&quot;subject&quot; id=&quot;subject&quot;
                   value=&quot;{{ form.subject.value|default_if_none:&#39;&#39; }}&quot;&gt;
        &lt;/div&gt;
        &lt;div class=&quot;mb-3&quot;&gt;
            &lt;label for=&quot;content&quot; class=&quot;form-label&quot;&gt;내용&lt;/label&gt;
            &lt;textarea class=&quot;form-control&quot; name=&quot;content&quot;
                      id=&quot;content&quot; rows=&quot;10&quot;&gt;{{ form.content.value|default_if_none:&#39;&#39; }}&lt;/textarea&gt;
        &lt;/div&gt;
        &lt;button type=&quot;submit&quot; class=&quot;btn btn-primary&quot;&gt;저장하기&lt;/button&gt;
    &lt;/form&gt;
&lt;/div&gt;
{% endblock %}</code></pre>
<ul>
<li><code>|default_if_none:&#39;</code> → form 데이터가 없을 경우 None 대신 공백으로 표시</li>
</ul>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/29ee417d-6c0b-4a0f-8d18-49aab3e7ae59/image.png" alt=""></p>
<h3 id="2-답변-등록">2. 답변 등록</h3>
<p><strong>forms.py</strong></p>
<pre><code class="language-python">from django import forms
from pybo.models import Question, Answer

(... 생략 ...)

class AnswerForm(forms.ModelForm):
    class Meta:
        model = Answer
        fields = [&#39;content&#39;]
        labels = {
            &#39;content&#39;: &#39;답변내용&#39;,
        }</code></pre>
<p><strong>views.py</strong></p>
<pre><code class="language-python">(... 생략 ...)
from django.http import HttpResponseNotAllowed
from .forms import QuestionForm, AnswerForm
(... 생략 ...)

def answer_create(request, question_id):
    &quot;&quot;&quot;
    pybo 답변등록
    &quot;&quot;&quot;
    question = get_object_or_404(Question, pk=question_id)
    if request.method == &quot;POST&quot;:
        form = AnswerForm(request.POST)
        if form.is_valid():
            answer = form.save(commit=False)
            answer.create_date = timezone.now()
            answer.question = question
            answer.save()
            return redirect(&#39;pybo:detail&#39;, question_id=question.id)
    else:
        return HttpResponseNotAllowed(&#39;Only POST is possible.&#39;)
    context = {&#39;question&#39;: question, &#39;form&#39;: form}
    return render(request, &#39;pybo/question_detail.html&#39;, context)</code></pre>
<p><strong>question_detail.html</strong></p>
<pre><code class="language-html">{% extends &#39;base.html&#39; %}
{% block content %}
&lt;div class=&quot;container my-3&quot;&gt;
    (... 생략 ...)
    &lt;form action=&quot;{% url &#39;pybo:answer_create&#39; question.id %}&quot; method=&quot;post&quot; class=&quot;my-3&quot;&gt;
        {% csrf_token %}
        &lt;!-- 오류표시 Start --&gt;
        {% if form.errors %}
        &lt;div class=&quot;alert alert-danger&quot; role=&quot;alert&quot;&gt;
            {% for field in form %}
            {% if field.errors %}
            &lt;div&gt;
                &lt;strong&gt;{{ field.label }}&lt;/strong&gt;
                {{ field.errors }}
            &lt;/div&gt;
            {% endif %}
            {% endfor %}
        &lt;/div&gt;
        {% endif %}
        &lt;!-- 오류표시 End --&gt;
        &lt;div class=&quot;mb-3&quot;&gt;
            &lt;textarea name=&quot;content&quot; id=&quot;content&quot; class=&quot;form-control&quot; rows=&quot;10&quot;&gt;&lt;/textarea&gt;
        &lt;/div&gt;
        &lt;input type=&quot;submit&quot; value=&quot;답변등록&quot; class=&quot;btn btn-primary&quot;&gt;
    &lt;/form&gt;
&lt;/div&gt;
{% endblock %}</code></pre>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/a84a2561-652a-4335-9d63-69b27dd48908/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL | 240106] Django - 부트스트랩, 템플릿 상속]]></title>
            <link>https://velog.io/@haeng_9/TIL-240106-Django-%EB%B6%80%ED%8A%B8%EC%8A%A4%ED%8A%B8%EB%9E%A9-%ED%85%9C%ED%94%8C%EB%A6%BF-%EC%83%81%EC%86%8D</link>
            <guid>https://velog.io/@haeng_9/TIL-240106-Django-%EB%B6%80%ED%8A%B8%EC%8A%A4%ED%8A%B8%EB%9E%A9-%ED%85%9C%ED%94%8C%EB%A6%BF-%EC%83%81%EC%86%8D</guid>
            <pubDate>Sat, 06 Jan 2024 07:47:39 GMT</pubDate>
            <description><![CDATA[<h2 id="부트스트랩bootstrap">부트스트랩(Bootstrap)</h2>
<hr>
<ul>
<li>트위터(Twitter)를 개발하면서 만들어졌고 현재 지속적으로 관리되고 있는 오픈소스 프로젝트</li>
<li>웹 페이지를 꾸밀 수 있는 프레임워크</li>
</ul>
<h3 id="1-부트스트랩-설치">1. 부트스트랩 설치</h3>
<p>1) <a href="https://getbootstrap.com/docs/5.1/getting-started/download/">https://getbootstrap.com/docs/5.1/getting-started/download/</a> 에서 파일 다운</p>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/ef67dd62-3d41-4388-a187-6de04ab19c0e/image.png" alt=""></p>
<p>2) 다운로드한 zip 파일 압축을 풀고 <code>bootstrap.min.css</code> 파일을 static 폴더로 copy</p>
<h3 id="2-부트스트랩-적용">2. 부트스트랩 적용</h3>
<p>1) 질문 목록에 부트스트랩 적용</p>
<p><strong>question_list.html</strong> </p>
<pre><code class="language-html">{% load static %}
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;{% static &#39;bootstrap.min.css&#39; %}&quot;&gt;
&lt;div class=&quot;container my-3&quot;&gt;
    &lt;table class=&quot;table&quot;&gt;
        &lt;thead&gt;
        &lt;tr class=&quot;table-dark&quot;&gt;
            &lt;th&gt;번호&lt;/th&gt;
            &lt;th&gt;제목&lt;/th&gt;
            &lt;th&gt;작성일시&lt;/th&gt;
        &lt;/tr&gt;
        &lt;/thead&gt;
        &lt;tbody&gt;
        {% if question_list %}
        {% for question in question_list %}
        &lt;tr&gt;
            &lt;td&gt;{{ forloop.counter }}&lt;/td&gt;
            &lt;td&gt;
                &lt;a href=&quot;{% url &#39;pybo:detail&#39; question.id %}&quot;&gt;{{ question.subject }}&lt;/a&gt;
            &lt;/td&gt;
            &lt;td&gt;{{ question.create_date }}&lt;/td&gt;
        &lt;/tr&gt;
        {% endfor %}
        {% else %}
        &lt;tr&gt;
            &lt;td colspan=&quot;3&quot;&gt;질문이 없습니다.&lt;/td&gt;
        &lt;/tr&gt;
        {% endif %}
        &lt;/tbody&gt;
    &lt;/table&gt;
&lt;/div&gt;</code></pre>
<p>→ 테이블 구조로 변경</p>
<ul>
<li><code>{{ forloop.counter }}</code> - for문의 현재 순서</li>
<li><code>class=&quot;container my-3&quot;</code>, <code>class=&quot;table&quot;</code>, <code>class=&quot;table-dark&quot;</code> - bootstrap 스타일에 정의된 클래스</li>
</ul>
<p>[ 질문 목록 화면 ]
<img src="https://velog.velcdn.com/images/haeng_9/post/28baaf2f-8318-4b88-9c53-1e32295dfae7/image.png" alt=""></p>
<p>2) 질문 상세 템플릿 수정</p>
<table>
<thead>
<tr>
<th>부트스트랩 클래스</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>card, card-body, card-text</td>
<td>부트스트랩 Card 컴포넌트</td>
</tr>
<tr>
<td>badge</td>
<td>부트스트랩 Badge 컴포넌트</td>
</tr>
<tr>
<td>form-control, form-label</td>
<td>부트스트랩 Form 컴포넌트</td>
</tr>
<tr>
<td>border-bottom</td>
<td>아래방향 테두리 선</td>
</tr>
<tr>
<td>my-3</td>
<td>상하 마진값 3</td>
</tr>
<tr>
<td>py-2</td>
<td>상하 패딩값 2</td>
</tr>
<tr>
<td>p-2</td>
<td>상하좌우 패딩값 2</td>
</tr>
<tr>
<td>d-flex justify-content-end</td>
<td>컴포넌트의 우측 정렬</td>
</tr>
<tr>
<td>bg-light</td>
<td>연회색 배경</td>
</tr>
<tr>
<td>text-dark</td>
<td>검은색 글씨</td>
</tr>
<tr>
<td>text-start</td>
<td>좌측 정렬</td>
</tr>
<tr>
<td>btn btn-primary</td>
<td>부트스트랩 버튼 컴포넌트</td>
</tr>
</tbody></table>
<p><strong>question_detail.html</strong></p>
<pre><code class="language-html">{% load static %}
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;{% static &#39;bootstrap.min.css&#39; %}&quot;&gt;
&lt;div class=&quot;container my-3&quot;&gt;
    &lt;!-- 질문 --&gt;
    &lt;h2 class=&quot;border-bottom py-2&quot;&gt;{{ question.subject }}&lt;/h2&gt;
    &lt;div class=&quot;card my-3&quot;&gt;
        &lt;div class=&quot;card-body&quot;&gt;
            &lt;div class=&quot;card-text&quot; style=&quot;white-space: pre-line;&quot;&gt;{{ question.content }}&lt;/div&gt;
            &lt;div class=&quot;d-flex justify-content-end&quot;&gt;
                &lt;div class=&quot;badge bg-light text-dark p-2&quot;&gt;
                    {{ question.create_date }}
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    &lt;!-- 답변 --&gt;
    &lt;h5 class=&quot;border-bottom my-3 py-2&quot;&gt;{{question.answer_set.count}}개의 답변이 있습니다.&lt;/h5&gt;
    {% for answer in question.answer_set.all %}
    &lt;div class=&quot;card my-3&quot;&gt;
        &lt;div class=&quot;card-body&quot;&gt;
            &lt;div class=&quot;card-text&quot; style=&quot;white-space: pre-line;&quot;&gt;{{ answer.content }}&lt;/div&gt;
            &lt;div class=&quot;d-flex justify-content-end&quot;&gt;
                &lt;div class=&quot;badge bg-light text-dark p-2&quot;&gt;
                    {{ answer.create_date }}
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
    {% endfor %}
    &lt;!-- 답변 등록 --&gt;
    &lt;form action=&quot;{% url &#39;pybo:answer_create&#39; question.id %}&quot; method=&quot;post&quot; class=&quot;my-3&quot;&gt;
        {% csrf_token %}
        &lt;div class=&quot;mb-3&quot;&gt;
            &lt;label for=&quot;content&quot; class=&quot;form-label&quot;&gt;답변내용&lt;/label&gt;
            &lt;textarea name=&quot;content&quot; id=&quot;content&quot; class=&quot;form-control&quot; rows=&quot;10&quot;&gt;&lt;/textarea&gt;
        &lt;/div&gt;
        &lt;input type=&quot;submit&quot; value=&quot;답변등록&quot; class=&quot;btn btn-primary&quot;&gt;
    &lt;/form&gt;
&lt;/div&gt;</code></pre>
<p>[ 부트스트랩 적용 전 ]
<img src="https://velog.velcdn.com/images/haeng_9/post/d45acce1-c8d2-4afa-a173-b9bfeb6ae99f/image.png" alt=""></p>
<p>[ 질문 상세 - 부트스트랩 적용 후 ]
<img src="https://velog.velcdn.com/images/haeng_9/post/e99cb769-bd75-437f-b746-1f909f5dd264/image.png" alt=""></p>
<h2 id="템플릿-상속">템플릿 상속</h2>
<hr>
<h3 id="1-표준-html-구조">1. 표준 HTML 구조</h3>
<pre><code class="language-html">&lt;!doctype html&gt;
&lt;html lang=&quot;ko&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;utf-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1, shrink-to-fit=no&quot;&gt;
    &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;/static/bootstrap.min.css&quot;&gt;
    &lt;title&gt;Django Practice&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
(... 생략 ...)
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<ul>
<li>html, head, body 엘리먼트가 있어야 함</li>
<li>CSS 파일 링크는 head 엘리먼트 안에 있어야 함</li>
<li>head 엘리먼트 안에는 meta, title 등이 포함되어야 함</li>
</ul>
<h3 id="2-템플릿-상속">2. 템플릿 상속</h3>
<ul>
<li>기본 틀이 되는 템플릿을 먼저 작성하고 다른 템플릿에서 그 템플릿을 상속해 사용하는 방법</li>
</ul>
<p><strong>base.html</strong> </p>
<pre><code class="language-html">{% load static %}
&lt;!doctype html&gt;
&lt;html lang=&quot;ko&quot;&gt;
&lt;head&gt;
    &lt;!-- Required meta tags --&gt;
    &lt;meta charset=&quot;utf-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1, shrink-to-fit=no&quot;&gt;
    &lt;!-- Bootstrap CSS --&gt;
    &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;{% static &#39;bootstrap.min.css&#39; %}&quot;&gt;
    &lt;!-- pybo CSS --&gt;
    &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;{% static &#39;style.css&#39; %}&quot;&gt;
    &lt;title&gt;Hello, pybo!&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;!-- 기본 템플릿 안에 삽입될 내용 Start --&gt;
{% block content %}
{% endblock %}
&lt;!-- 기본 템플릿 안에 삽입될 내용 End --&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<ul>
<li><code>{% block content %}</code> , <code>{% endblock %}</code> →  <code>base.html</code>을 상속한 템플릿에서 개별적으로 구현해야 하는 영역</li>
</ul>
<p><strong>question_list.html</strong></p>
<pre><code class="language-html">{% extends &#39;base.html&#39; %}
{% block content %}
&lt;div class=&quot;container my-3&quot;&gt;
    &lt;table class=&quot;table&quot;&gt;

        (... 생략 ...)

    &lt;/table&gt;
&lt;/div&gt;
{% endblock %}</code></pre>
<p><strong>question_detail.html</strong></p>
<pre><code class="language-html">{% extends &#39;base.html&#39; %}
{% block content %}
&lt;div class=&quot;container my-3&quot;&gt;
    &lt;h2 class=&quot;border-bottom py-2&quot;&gt;{{ question.subject }}&lt;/h2&gt;

    (... 생략 ...)

    &lt;/form&gt;
&lt;/div&gt;
{% endblock %}</code></pre>
<h3 id="1-표준-html-구조-1">1. 표준 HTML 구조</h3>
<pre><code class="language-html">&lt;!doctype html&gt;
&lt;html lang=&quot;ko&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;utf-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1, shrink-to-fit=no&quot;&gt;
    &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;/static/bootstrap.min.css&quot;&gt;
    &lt;title&gt;Django Practice&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
(... 생략 ...)
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<ul>
<li>html, head, body 엘리먼트가 있어야 함</li>
<li>CSS 파일 링크는 head 엘리먼트 안에 있어야 함</li>
<li>head 엘리먼트 안에는 meta, title 등이 포함되어야 함</li>
</ul>
<h3 id="2-템플릿-상속-1">2. 템플릿 상속</h3>
<ul>
<li>기본 틀이 되는 템플릿을 먼저 작성하고 다른 템플릿에서 그 템플릿을 상속해 사용하는 방법</li>
</ul>
<p><strong>base.html</strong> </p>
<pre><code class="language-html">{% load static %}
&lt;!doctype html&gt;
&lt;html lang=&quot;ko&quot;&gt;
&lt;head&gt;
    &lt;!-- Required meta tags --&gt;
    &lt;meta charset=&quot;utf-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1, shrink-to-fit=no&quot;&gt;
    &lt;!-- Bootstrap CSS --&gt;
    &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;{% static &#39;bootstrap.min.css&#39; %}&quot;&gt;
    &lt;!-- pybo CSS --&gt;
    &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;{% static &#39;style.css&#39; %}&quot;&gt;
    &lt;title&gt;Hello, pybo!&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;!-- 기본 템플릿 안에 삽입될 내용 Start --&gt;
{% block content %}
{% endblock %}
&lt;!-- 기본 템플릿 안에 삽입될 내용 End --&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<ul>
<li><code>{% block content %}</code> , <code>{% endblock %}</code> →  <code>base.html</code>을 상속한 템플릿에서 개별적으로 구현해야 하는 영역</li>
</ul>
<p><strong>question_list.html</strong></p>
<pre><code class="language-html">{% extends &#39;base.html&#39; %}
{% block content %}
&lt;div class=&quot;container my-3&quot;&gt;
    &lt;table class=&quot;table&quot;&gt;

        (... 생략 ...)

    &lt;/table&gt;
&lt;/div&gt;
{% endblock %}</code></pre>
<p><strong>question_detail.html</strong></p>
<pre><code class="language-html">{% extends &#39;base.html&#39; %}
{% block content %}
&lt;div class=&quot;container my-3&quot;&gt;
    &lt;h2 class=&quot;border-bottom py-2&quot;&gt;{{ question.subject }}&lt;/h2&gt;

    (... 생략 ...)

    &lt;/form&gt;
&lt;/div&gt;
{% endblock %}</code></pre>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/8d5e2cc5-e885-43d7-9b7c-8ab03b408ae9/image.png" alt=""></p>
<p>→ 소스 코드를 보면 보여지는 것은 같지만 상속된 것을 확인할 수 있음</p>
<h3 id="3-stylecss">3. style.css</h3>
<ul>
<li>부트스트랩 적용으로 인해 <code>style.css</code>의 내용은 필요가 없어졌으므로 기존 내용을 모두 삭제</li>
</ul>
<p>→ 소스 코드를 보면 보여지는 것은 같지만 상속된 것을 확인할 수 있음</p>
<h3 id="3-stylecss-1">3. style.css</h3>
<ul>
<li>부트스트랩 적용으로 인해 <code>style.css</code>의 내용은 필요가 없어졌으므로 기존 내용을 모두 삭제</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL | 240105] Django - 데이터 저장, Static]]></title>
            <link>https://velog.io/@haeng_9/TIL-240105-Django-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%80%EC%9E%A5-Static</link>
            <guid>https://velog.io/@haeng_9/TIL-240105-Django-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%80%EC%9E%A5-Static</guid>
            <pubDate>Fri, 05 Jan 2024 01:13:33 GMT</pubDate>
            <description><![CDATA[<h2 id="데이터-저장">데이터 저장</h2>
<hr>
<h3 id="1-답변등록-폼">1. 답변등록 폼</h3>
<p><strong>question_detail.html</strong></p>
<pre><code class="language-html">&lt;h1&gt;{{ question.subject }}&lt;/h1&gt;
&lt;div&gt;
    {{ question.content }}
&lt;/div&gt;
&lt;form action=&quot;{% url &#39;pybo:answer_create.id %}&quot; method=&quot;post&quot;&gt;
{% csrf_token %}
&lt;textarea name=&quot;content&quot; id=&quot;content&quot; rows=&quot;15&quot;&gt;&lt;/textarea&gt;
&lt;input type=&quot;submit&quot; value=&quot;답변등록&quot;&gt;
&lt;/form&gt;</code></pre>
<ul>
<li><code>{% csrf_token %}</code> - 보안 관련 항목으로 Post 요청시 form 태그에 csrf_token이 없으면 장고는 에러</li>
</ul>
<h3 id="2-url-매핑">2. URL 매핑</h3>
<p><strong>pybo/urls.py</strong></p>
<pre><code class="language-python">from django.urls import path

from . import views

app_name = &#39;pybo&#39;

urlpatterns = [
    path(&#39;&#39;, views.index, name=&#39;index&#39;),
    path(&#39;&lt;int:question_id&gt;/&#39;, views.detail, name=&#39;detail&#39;),
        path(&#39;answer/create/&lt;int:qustion_id&gt;&#39;, views.answer_create, name=&#39;answer_create&#39;),
]</code></pre>
<p>→ 매핑 등록</p>
<h3 id="3-뷰-함수">3. 뷰 함수</h3>
<p><strong>pybo/views.py</strong></p>
<pre><code class="language-python">from django.shortcuts import render, get_object_or_404, redirect
from django.utils import timezone
from .models import Question

(... 생략 ...)

def answer_create(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    question.answer_set.create(content=request.POST.get(&#39;content&#39;), create_date=timezone.now()) #답변 생성 (FK 이용)
    return redirect(&#39;pybo:detail&#39;, question_id=question.id)</code></pre>
<ul>
<li>answer_create 함수 추가</li>
<li>redirect → 페이지 이동을 위한 함수</li>
</ul>
<h3 id="4-답변-저장">4. 답변 저장</h3>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/b71ff4e1-d886-43f3-8f5e-d3bb62295f16/image.png" alt=""></p>
<h3 id="5-답변-조회">5. 답변 조회</h3>
<ul>
<li>등록된 답변을 상세 화면에 표시</li>
</ul>
<p><strong>pybo/question_detail.html</strong></p>
<pre><code class="language-html">&lt;h1&gt;{{ question.subject }}&lt;/h1&gt;
&lt;div&gt;
    {{ question.content }}
&lt;/div&gt;
&lt;h5&gt;{{ question.answer_set.count }}개의 답변이 있습니다.&lt;/h5&gt; #답변의 개수
&lt;div&gt;
    &lt;ul&gt;
    {% for answer in question.answer_set.all %}
        &lt;li&gt;{{ answer.content }}&lt;/li&gt;
    {% endfor %}
    &lt;/ul&gt;
&lt;/div&gt;
&lt;form action=&quot;{% url &#39;pybo:answer_create&#39; question.id %}&quot; method=&quot;post&quot;&gt;
{% csrf_token %}
&lt;textarea name=&quot;content&quot; id=&quot;content&quot; rows=&quot;15&quot;&gt;&lt;/textarea&gt;
&lt;input type=&quot;submit&quot; value=&quot;답변등록&quot;&gt;
&lt;/form&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/d7654f94-da7d-4d68-ab2b-67e175752cb2/image.png" alt=""></p>
<h2 id="스태틱static">스태틱(Static)</h2>
<hr>
<ul>
<li>화면에 디자인 적용하기위한 스타일시트 생성, 스타일시트는 static 디렉터리에 저장해야 함.</li>
</ul>
<h3 id="1-static-directory">1. <code>static</code> directory</h3>
<ul>
<li><p>static 폴더 생성(<code>mkdir static</code>) 후 <strong>config/settings.py</strong>에 등록</p>
</li>
<li><p><strong>settings.py</strong></p>
<pre><code class="language-python">  (... 생략 ...)

  STATIC_URL = &#39;static/&#39;
  STATICFILES_DIRS = [
      BASE_DIR / &#39;static&#39;,
  ]

  (... 생략 ...)</code></pre>
</li>
</ul>
<h3 id="2-stylesheet">2. Stylesheet</h3>
<p><strong>static/style.css</strong></p>
<pre><code class="language-css">textarea {
    width:100%;
}

input[type=submit] {
    margin-top:10px;
}</code></pre>
<h3 id="3-템플릿에-스타일-적용">3. 템플릿에 스타일 적용</h3>
<p><strong>question_detail.html</strong></p>
<pre><code class="language-html">{% load static %} 
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;{% static &#39;style.css&#39; %}&quot;&gt;

(... 생략 ...)</code></pre>
<ul>
<li><code>{% load static %}</code> - 최상단에 삽입해 <code>{% static ... %}</code>와 같은 템플릿 태그를 사용할수 있도록 함.</li>
</ul>
<p><img src="blob:https://velog.io/179597b9-9b8e-4132-91af-893c06c89c78" alt="업로드중.."></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL | 240103] Django - 관리자, 템플릿, URL별칭]]></title>
            <link>https://velog.io/@haeng_9/TIL-240103-Django-%EA%B4%80%EB%A6%AC%EC%9E%90-%ED%85%9C%ED%94%8C%EB%A6%BF-URL%EB%B3%84%EC%B9%AD</link>
            <guid>https://velog.io/@haeng_9/TIL-240103-Django-%EA%B4%80%EB%A6%AC%EC%9E%90-%ED%85%9C%ED%94%8C%EB%A6%BF-URL%EB%B3%84%EC%B9%AD</guid>
            <pubDate>Thu, 04 Jan 2024 14:19:04 GMT</pubDate>
            <description><![CDATA[<h2 id="장고-관리자">장고 관리자</h2>
<hr>
<ol>
<li>Super User 생성<ul>
<li><code>python [manage.py](http://manage.py) createsuperuser</code></li>
</ul>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/5c6e1e57-11a8-414e-87be-212d1ff669d5/image.png" alt=""></p>
<ol start="2">
<li>장고 관리자 화면<ul>
<li><a href="http://localhost:8000/admin/"><code>http://localhost:8000/admin/</code></a></li>
</ul>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/468768e2-370d-4969-9910-05f5a6db8669/image.png" alt=""></p>
<ol start="3">
<li><p>모델 관리</p>
<ul>
<li><p>Question 모델을 관리자에 등록</p>
<p>  <strong>pybo/admin.py</strong></p>
<pre><code class="language-python">  from django.contrib import admin
  from .models import Question

  # Register your models here.
  admin.site.register(Question)</code></pre>
</li>
</ul>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/3d915786-e756-4116-a813-b265ce32a3e6/image.png" alt=""></p>
<pre><code>    - 관리자 화면에서 Question 모델 질문 생성, 조회, 수정, 삭제 가능</code></pre><p><img src="https://velog.velcdn.com/images/haeng_9/post/ef9e4eb4-4524-4d28-adde-537a450dd844/image.png" alt=""></p>
<ol start="4">
<li><p>모델 검색</p>
<ul>
<li><p>관리자 화면에서 subject로 질문 데이터 검색하기</p>
</li>
<li><p>*<a href="http://admin.py">admin.py</a>** (수정)</p>
<pre><code class="language-python">from django.contrib import admin
from .models import Question

# Register your models here.
class QuestionAdmin(admin.ModelAdmin):
   search_fields = [&#39;subject&#39;]

admin.site.register(Question, QuestionAdmin)</code></pre>
</li>
</ul>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/f52c593e-8ef9-41bd-aea1-6af685bdd6f8/image.png" alt=""></p>
<h2 id="조회와-템플릿">조회와 템플릿</h2>
<hr>
<blockquote>
<p>질문 목록과 질문 상세 기능 구현</p>
</blockquote>
<h3 id="1-질문-목록">1. 질문 목록</h3>
<ul>
<li><p>메인 페이지 수정</p>
<p>  <strong>views.py</strong></p>
<pre><code class="language-python">  from django.shortcuts import render
  from .models import Question
  #from django.http import HttpResponse 

  # Create your views here.
  def index(request):
      question_list = Question.objects.order_by(&#39;-create_date&#39;) #order_by : 조회 결과 정렬, - : 역방향 정렬
      context = {&#39;question_list&#39;: question_list}
      return render(request, &#39;pybo/question_list.html&#39;, context) #render: 파이썬 데이터를 템플릿에 적용하여 HTML로 반환</code></pre>
<ul>
<li>템플릿 파일 - HTML파일과 비슷하지만 파이썬 데이터를 읽어서 사용할 수 있는 HTML 파일</li>
</ul>
</li>
</ul>
<h3 id="템플릿-디렉터리"><strong>템플릿 디렉터리</strong></h3>
<ul>
<li>render 에서 사용한 <code>pybo/question_list.html</code> 파일 작성 전, 템플릿 저장 디렉터리 설정</li>
</ul>
<p><strong>settings.py</strong></p>
<pre><code class="language-python">(... 생략 ...)
TEMPLATES = [
    {
        &#39;BACKEND&#39;: &#39;django.template.backends.django.DjangoTemplates&#39;,
        &#39;DIRS&#39;: [BASE_DIR / &#39;templates&#39;], # 추가
        &#39;APP_DIRS&#39;: True,
        &#39;OPTIONS&#39;: {
            &#39;context_processors&#39;: [
                &#39;django.template.context_processors.debug&#39;,
                &#39;django.template.context_processors.request&#39;,
                &#39;django.contrib.auth.context_processors.auth&#39;,
                &#39;django.contrib.messages.context_processors.messages&#39;,
            ],
        },
    },
]
(... 생략 ...)</code></pre>
<ul>
<li><p>디렉터리 생성</p>
<p>  <code>mkdir templates</code> </p>
<ul>
<li><p>하나의 웹에서 여러 개의 앱을 사용할 수도 있을므로 경로를<code>projects/mysite/pybo/templates</code>이 아닌 <code>/projects/mysite/templates/pybo</code> 디렉터리를 사용</p>
</li>
<li><p>공용 템플릿 - <code>projects/mysite/templates</code></p>
</li>
<li><p><em>django_prac1/templates/pybo/question_list.html*</em></p>
<pre><code class="language-html">{% if question_list %}
   &lt;ul&gt;
   {% for question in question_list %}
       &lt;li&gt;&lt;a href=&quot;/pybo/{{ question.id }}/&quot;&gt;{{ question.subject }}&lt;/a&gt;&lt;/li&gt;
   {% endfor %}
   &lt;/ul&gt;
{% else %}
   &lt;p&gt;질문이 없습니다.&lt;/p&gt;
{% endif %}</code></pre>
</li>
</ul>
</li>
</ul>
<h3 id="템플릿-태그">템플릿 태그</h3>
<p><strong>1. 분기</strong></p>
<pre><code class="language-python">{% if 조건문1 %}
    &lt;p&gt;조건문1에 해당되는 경우&lt;/p&gt;
{% elif 조건문2 %}
    &lt;p&gt;조건문2에 해당되는 경우&lt;/p&gt;
{% else %}
    &lt;p&gt;조건문1, 2에 모두 해당되지 않는 경우&lt;/p&gt;
{% endif %}</code></pre>
<p><strong>2. 반복</strong></p>
<pre><code class="language-python">{% for item in list %}
    &lt;p&gt;순서: {{ forloop.counter }} &lt;/p&gt;
    &lt;p&gt;{{ item }}&lt;/p&gt;
{% endfor %}</code></pre>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/b634cb19-9c6b-4f9d-b951-796eae7de44b/image.png" alt=""></p>
<p><strong>3. 객체 출력</strong></p>
<pre><code class="language-python"># 객체 출력
{{ 객체 }}
# 객체 속성 출력 
{{ 객체.속성 }}</code></pre>
<h3 id="2-질문-상세">2. 질문 상세</h3>
<h3 id="pybourlspy">pybo/urls.py</h3>
<pre><code class="language-python">from django.urls import path

from . import views

urlpatterns = [
    path(&#39;&#39;, views.index),
    path(&#39;&lt;int:question_id&gt;/&#39;, views.detail),
]</code></pre>
<h3 id="pyboviewspy">pybo/views.py</h3>
<pre><code class="language-python"># detail 함수 추가
def detail(request, question_id):
    question = Question.objects.get(id=question_id)
    context = {&#39;question&#39;: question}
    return render(request, &#39;pybo/question_detail.html&#39;, context)</code></pre>
<h3 id="question_detailhtml">question_detail.html</h3>
<pre><code class="language-html">&lt;h1&gt;{{ question.subject }}&lt;/h1&gt;
&lt;div&gt;
    {{question.content}}
&lt;/div&gt;</code></pre>
<h3 id="3-오류-페이지">3. 오류 페이지</h3>
<ul>
<li><p>존재하지 않는 데이터 요청시 500 에러 발생 → 404 에러 페이지를 리턴하는 것이 좋음</p>
<p>  <strong>views.py</strong></p>
<pre><code class="language-python">  from django.shortcuts import render, get_object_or_404
  from .models import Question
  #from django.http import HttpResponse 

  # Create your views here.
  def index(request):
      question_list = Question.objects.order_by(&#39;-create_date&#39;)
      context = {&#39;question_list&#39;: question_list}
      return render(request, &#39;pybo/question_list.html&#39;, context)

  def detail(request, question_id):
      question = get_object_or_404(Question, pk = question_id)
      context = {&#39;question&#39;: question}
      return render(request, &#39;pybo/question_detail.html&#39;, context)</code></pre>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/11983de1-d7c9-434b-a599-39e081d63326/image.png" alt=""></p>
<ul>
<li><code>200</code> - 성공</li>
<li><code>500</code> - Internal Server Error</li>
<li><code>404</code> - Resource Not Found Error</li>
</ul>
<h2 id="url-별칭">URL 별칭</h2>
<hr>
<h3 id="1-url-하드코딩">1. URL 하드코딩</h3>
<p>question_list.html </p>
<pre><code class="language-html">&lt;li&gt;&lt;a href=&quot;/pybo/{{ question.id }}/&quot;&gt;{{ question.subject }}&lt;/a&gt;&lt;/li&gt;</code></pre>
<p>→ url 리팩토링이 빈번해 구조 변경이 일일이 템플릿을 찾아가 수정해야 함</p>
<p>→ 따라서 실제 링크 대신 링크 주소가 매핑되어 있는 별칭 사용하기</p>
<h3 id="2-url-별칭">2. URL 별칭</h3>
<p>URL 매핑에 name 속성 부여</p>
<p><strong>pybo/urls.py</strong></p>
<pre><code class="language-python">from django.urls import path

from . import views

urlpatterns = [
    path(&#39;&#39;, views.index, name=&#39;index&#39;),
    path(&#39;&lt;int:question_id&gt;/&#39;, views.detail, name=&#39;detail&#39;),
]</code></pre>
<p><strong>question_list.html</strong></p>
<pre><code class="language-html">{% if question_list %}
    &lt;ul&gt;
    {% for question in question_list %}
        &lt;li&gt;&lt;a href=&quot;{% url &#39;detail&#39; question.id %}&quot;&gt;{{ question.subject }}&lt;/a&gt;&lt;/li&gt;
    {% endfor %}
    &lt;/ul&gt;
{% else %}
    &lt;p&gt;질문이 없습니다.&lt;/p&gt;
{% endif %}</code></pre>
<h3 id="3-url-네임-스페이스">3. URL 네임 스페이스</h3>
<ul>
<li>하나의 프로젝트에 여러 앱을 사용할 때 동일한 URL별칭이 생길 수 있으므로 app_name 변수 지정</li>
</ul>
<p><strong>pybo/urls.py</strong></p>
<pre><code class="language-python">from django.urls import path

from . import views

app_name = &#39;pybo&#39;

urlpatterns = [
    path(&#39;&#39;, views.index, name=&#39;index&#39;),
    path(&#39;&lt;int:question_id&gt;/&#39;, views.detail, name=&#39;detail&#39;),
]</code></pre>
<p><strong>question_list.html</strong></p>
<ul>
<li><code>{% url &#39;pybo:detail&#39; question.id %}</code></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL | 231229] Django - 모델(Model)]]></title>
            <link>https://velog.io/@haeng_9/TIL-231229-Django-%EB%AA%A8%EB%8D%B8Model</link>
            <guid>https://velog.io/@haeng_9/TIL-231229-Django-%EB%AA%A8%EB%8D%B8Model</guid>
            <pubDate>Tue, 02 Jan 2024 00:18:39 GMT</pubDate>
            <description><![CDATA[<ul>
<li>데이터 베이스 처리</li>
<li><code>python [manage.py](http://manage.py) migrate</code> - 앱이 필요로 하는 데이터베이스 테이블 생성</li>
</ul>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/3966b043-4f4a-4039-bf3d-6b30c0042f0a/image.png" alt=""></p>
<p><strong>models.py</strong></p>
<pre><code class="language-python">from django.db import models

# Create your models here.
class Question(models.Model):
    subject = models.CharField(max_length=200)
    content = models.TextField()
    create_date = models.DateTimeField()

class Answer(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    content = models.TextField()
    create_date = models.DateTimeField()</code></pre>
<p><strong>테이블 생성</strong></p>
<ul>
<li>pybo 앱을 <a href="http://settings.py">settings.py</a>의 INSTALLED_APPS 항목에 추가</li>
</ul>
<pre><code class="language-python">INSTALLED_APPS = [
    &#39;pybo.apps.PyboConfig&#39;, #추가 -&gt; pybo/app.py 에 있는 클래스
    &#39;django.contrib.admin&#39;,
    &#39;django.contrib.auth&#39;,
    &#39;django.contrib.contenttypes&#39;,
    &#39;django.contrib.sessions&#39;,
    &#39;django.contrib.messages&#39;,
    &#39;django.contrib.staticfiles&#39;,
]</code></pre>
<ul>
<li><code>python [manage.py](http://mange.py) makemigrations</code> - 모델 신규 생성, 변경시 <code>migrate</code> 보다 먼저 수행</li>
<li><code>python [mange.py](http://mange.py) migrate</code> - 모델 속성 변경시 필요,메서드 추가시에는 필요 없음.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/005f4412-4291-499a-a1fe-ff84e33e641b/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/0713f829-ab57-4f33-9b4c-93dcd7e5948d/image.png" alt=""></p>
<ul>
<li><code>python [manage.py](http://manage.py) sqlmigrate {앱이름} {작업번호}</code> - 어떤 쿼리가 실행되는지 확인만</li>
</ul>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/066989df-2715-4795-9a1d-bf3c3f0b291f/image.png" alt=""></p>
<p><strong>Shell로 모델 사용하기</strong></p>
<ul>
<li><code>python [manage.py](http://manage.py) shell</code></li>
</ul>
<pre><code class="language-python">
from pybo.models import Question, Answer

# 질문 생성 (Question)
from django.utils import timezone
q = Question(subject=&#39;pybo가 무엇인가요?&#39;, content=&#39;pybo에 대해서 알고 싶습니다.&#39;, create_date=timezone.now())
q.save()

q.id

# 질문 조회
Question.objects.all()

# -- filter를 활용한 조회 : 조건에 해당되는 데이터 모두 return
# -- get을 활용한 조회 : 유일 값. 조건 만족 데이터가 없을 시 오류 발생
Question.objects.filter(id=1)
Question.objects.get(id=1)
Question.objects.filter(subject__contains=&#39;장고&#39;)

# 질문 수정
q = Question.objects.get(id=2)
q.subject = &#39;Django Model Question&#39;
q.save() # 반드시 해야 반영됨. 

# 질문 삭제 
q = Question.objects.get(id=1)
q.delete()

# 답변 작성
q = Question.objects.get(id=2)
from django.utils import timezone
a = Answer(question=q, content=&#39;네 자동으로 생성됩니다.&#39;, create_date=timezone.now())
a.save()

# 답변 조회
a = Answer.objects.get(id=1)
a.question #객체 a를 사용해 답변에 연결된 질문도 조회
q.answer_set.all() #질문에 연결된 답 가져오기 </code></pre>
<ul>
<li><code>속성__contains= &#39; &#39;</code> - 속성에 특정 값이 있는지.</li>
<li><code>연결모델명_set</code> - 자주 쓰임, 연결된 것 가져오기</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL | 231228] Django - 기본세팅, 앱(App)]]></title>
            <link>https://velog.io/@haeng_9/TIL-231228-Django-%EA%B8%B0%EB%B3%B8%EC%84%B8%ED%8C%85-%EC%95%B1App</link>
            <guid>https://velog.io/@haeng_9/TIL-231228-Django-%EA%B8%B0%EB%B3%B8%EC%84%B8%ED%8C%85-%EC%95%B1App</guid>
            <pubDate>Sat, 30 Dec 2023 13:54:37 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>공부 자료 : <a href="https://wikidocs.net/book/4223">https://wikidocs.net/book/4223</a> 점프 투 장고</p>
</blockquote>
<h2 id="django-기본-세팅">Django 기본 세팅</h2>
<hr>
<p>[참고]</p>
<p><a href="https://velog.io/@rjsekaehdhkw/Visual-Studio-Code%EC%97%90%EC%84%9C-Django-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B8%B0%EB%B3%B8-%EC%85%8B%ED%8C%85-%ED%95%98%EB%8A%94-%EB%B2%95">Visual Studio Code에서  Django 프로젝트 기본 셋팅 하는 법(파일 생성부터 Git 설정까지)</a></p>
<ul>
<li>Django 설치<ul>
<li><code>pip install django</code></li>
</ul>
</li>
<li>DRF 설치<ul>
<li><code>pip install djangorestframework</code></li>
</ul>
</li>
<li>Django 프로젝트 생성<ul>
<li>디렉토리 생성 후 <code>django-admin startproject config .</code></li>
</ul>
</li>
<li>서버 구동<ul>
<li><code>python [manage.py](http://manage.py/) runserver</code></li>
</ul>
</li>
</ul>
<h2 id="django-기본-요소">Django 기본 요소</h2>
<hr>
<p>🔥 장고 개발 흐름</p>
<p>(1) 브라우저에서 로컬 서버로 <code>http://localhost:8000/pybo</code> 페이지를 요청</p>
<p>(2) urls.py 파일에서 <code>/pybo</code> URL 매핑을 확인하여 views.py 파일의 index 함수를 호출</p>
<p>(3) 호출한 결과를 브라우저에 반영</p>
<h2 id="앱app">앱(App)</h2>
<hr>
<ul>
<li>프로젝트에 기능 추가</li>
<li><code>django-admin startapp 앱이름</code></li>
</ul>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/90f987b5-fa70-4b3e-a535-f55a3011818d/image.png" alt=""></p>
<p>[ <strong><a href="http://urls.py">urls.py</a></strong> ]</p>
<ul>
<li>페이지 요청이 발생하면 가장 먼저 호출되는 파일</li>
</ul>
<pre><code class="language-python">from django.contrib import admin
from django.urls import path

from pybo import views # 추가

urlpatterns = [
    path(&#39;admin/&#39;, admin.site.urls),
    path(&#39;앱이름/&#39;, views.index), # 이 부분 추가하기. view.py 파일의 index 함수 
]</code></pre>
<p><strong>[ <a href="http://views.py">views.py</a> ]</strong></p>
<ul>
<li>index 함수 추가</li>
</ul>
<pre><code class="language-python">from django.http import HttpResponse

def index(request):
    return HttpResponse(&quot;안녕하세요 pybo에 오신것을 환영합니다.&quot;)</code></pre>
<p><strong>URL 분리</strong></p>
<p>1) <a href="http://urls.py">urls.py</a> 파일 수정</p>
<pre><code class="language-python">from django.contrib import admin
from django.urls import path, include #수정

urlpatterns = [
    path(&#39;admin/&#39;, admin.site.urls),
    path(&#39;pybo/&#39;, include(&#39;pybo.urls&#39;)), #수정
]</code></pre>
<p>2) pybo/urls.py 생성</p>
<pre><code class="language-python">from django.urls import path

from . import views

urlpatterns = [
    path(&#39;&#39;, views.index),
]</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[GenAI] DreamGaussian (1)- CUDA, Pytorch 설치 ]]></title>
            <link>https://velog.io/@haeng_9/GenAI-DreamGaussian-1-CUDA-Pytorch-%EC%84%A4%EC%B9%98</link>
            <guid>https://velog.io/@haeng_9/GenAI-DreamGaussian-1-CUDA-Pytorch-%EC%84%A4%EC%B9%98</guid>
            <pubDate>Mon, 09 Oct 2023 07:59:41 GMT</pubDate>
            <description><![CDATA[<h1 id="🌜dreamgaussian🌜">🌜DreamGaussian🌜</h1>
<hr>
<p><a href="https://dreamgaussian.github.io/">https://dreamgaussian.github.io/</a></p>
<p>최근에 나온 기술</p>
<h1 id="수정-중-">(수정 중... )</h1>
<p>다음 유튜브 링크로 들어가면 Local에서 어떻게 사용하는지 나와있다.</p>
<p><a href="https://youtu.be/_hy6iCq9f9o?si=_CBp5ShxLykucnSY"><img src="https://velog.velcdn.com/images/haeng_9/post/5accccb2-5624-44b4-90f8-a8d5270e1700/image.png" alt="Video Label">
</a></p>
<p>따라서 하던 중 해당 에러가 발생했고 환경이 맞지 않아 발생하는 에러임을 알 수 있었다.</p>
<p>Github를 보면 </p>
<pre><code>Tested on:

Ubuntu 22 with torch 1.12 &amp; CUDA 11.6 on a V100.
Windows 10 with torch 2.1 &amp; CUDA 12.1 on a 3070.</code></pre><p>이렇게 되어 있고, 현재 노트북이 Windows10에 3070이기 때문에 torch 2.1, CUDA 12.1 을 설치했다. </p>
<h2 id="cuda-121-설치-🔧">CUDA 12.1 설치 🔧</h2>
<hr>
<h3 id="로컬에-cuda-121-설치">로컬에 CUDA 12.1 설치</h3>
<p><strong>1. 내 로컬 환경 확인</strong>
<img src="https://velog.velcdn.com/images/haeng_9/post/c9d260cf-f46e-4783-8916-9c7b1a83e3dd/image.png" alt=""> 
기존에 11.7 버전이 설치되어 있다.</p>
<p><strong>2. CUDA Toolkit 12.1 설치</strong></p>
<blockquote>
<p><a href="https://developer.nvidia.com/cuda-toolkit-archive">https://developer.nvidia.com/cuda-toolkit-archive</a></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/24d2c875-0f22-4b84-bade-24694a7fcd2a/image.png" alt=""></p>
<p><strong>2. cuDNN 설치</strong></p>
<blockquote>
<p><a href="https://developer.nvidia.com/rdp/cudnn-download">https://developer.nvidia.com/rdp/cudnn-download</a></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/c8f19d97-0645-4d8f-985e-33c4a357524d/image.png" alt=""></p>
<p>CUDA 12.1을 설치했기 때문에 위에서 보는 것처럼 이에 맞추어 v8.9.5 윈도우 버전으로 다운 받았다.</p>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/49b13af6-5565-40c2-8105-0c4f1a8cc720/image.png" alt=""></p>
<p>그리고 위의 CUDA 설치 경로에 <code>bin</code>, <code>include</code>, <code>lib</code>에 맞게 다운받은 파일들을 넣어주면 끝.</p>
<p>cmd에서 <code>nvidia-smi</code> 를 입력하면
<img src="https://velog.velcdn.com/images/haeng_9/post/921de657-f47d-46a2-83b0-ef437c4a540d/image.png" alt="">
다음과 같이 CUDA Version이 12.1이 된 것을 확인할 수 있다:)</p>
<h2 id="2-torch-21-설치-🔧">2. Torch 2.1 설치 🔧</h2>
<hr>
<blockquote>
<p><a href="https://pytorch.org/get-started/locally/">https://pytorch.org/get-started/locally/</a></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/4ae38d28-791b-49d4-9f1d-cd0dcaa9a450/image.png" alt="">
위의 링크에서 본인 환경에 맞추어 선택 후, Run this Command에 나와있는 명령어로 설치할 수 있다.</p>
<ol>
<li><code>pip</code> 설치할 경우
<code>pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121</code></li>
</ol>
<ol start="2">
<li>conda 가상환경에 설치할 경우
<code>conda install pytorch torchvision torchaudio pytorch-cuda=12.1 -c pytorch -c nvidia</code></li>
</ol>
<p><strong>Pytorch 설치 확인</strong></p>
<pre><code class="language-python"></code></pre>
<p>[cmd] 
<img src="https://velog.velcdn.com/images/haeng_9/post/3d3ee168-524f-4551-a82a-2daf21572284/image.png" alt=""></p>
<p>[conda 가상환경]
<img src="https://velog.velcdn.com/images/haeng_9/post/421016de-55c3-4db1-8468-706fdd48253e/image.png" alt=""></p>
<p>=&gt; 모두 정상적으로 설치된 것을 확인할 수 있다 😄</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스]코딩테스트 연습 | 문자열 내 마음대로 정렬하기]]></title>
            <link>https://velog.io/@haeng_9/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%97%B0%EC%8A%B5-%EB%AC%B8%EC%9E%90%EC%97%B4-%EB%82%B4-%EB%A7%88%EC%9D%8C%EB%8C%80%EB%A1%9C-%EC%A0%95%EB%A0%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@haeng_9/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%97%B0%EC%8A%B5-%EB%AC%B8%EC%9E%90%EC%97%B4-%EB%82%B4-%EB%A7%88%EC%9D%8C%EB%8C%80%EB%A1%9C-%EC%A0%95%EB%A0%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 26 Aug 2023 12:52:02 GMT</pubDate>
            <description><![CDATA[<h3 id="문제--문자열-내-마음대로-정렬하기">문제 | 문자열 내 마음대로 정렬하기</h3>
<hr>
<blockquote>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/12915">https://school.programmers.co.kr/learn/courses/30/lessons/12915</a></p>
</blockquote>
<p><strong>문제 설명</strong>
문자열로 구성된 리스트 strings와, 정수 n이 주어졌을 때, 각 문자열의 인덱스 n번째 글자를 기준으로 오름차순 정렬하려 합니다. 예를 들어 strings가 [&quot;sun&quot;, &quot;bed&quot;, &quot;car&quot;]이고 n이 1이면 각 단어의 인덱스 1의 문자 &quot;u&quot;, &quot;e&quot;, &quot;a&quot;로 strings를 정렬합니다.</p>
<p><strong>제한 조건</strong>
strings는 길이 1 이상, 50이하인 배열입니다.
strings의 원소는 소문자 알파벳으로 이루어져 있습니다.
strings의 원소는 길이 1 이상, 100이하인 문자열입니다.
모든 strings의 원소의 길이는 n보다 큽니다.
인덱스 1의 문자가 같은 문자열이 여럿 일 경우, 사전순으로 앞선 문자열이 앞쪽에 위치합니다.</p>
<h3 id="입출력-예시">입출력 예시</h3>
<hr>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/1986db37-42b8-4b62-93a5-815f12621c82/image.png" alt=""></p>
<p>[입출력 예 1]
&quot;sun&quot;, &quot;bed&quot;, &quot;car&quot;의 1번째 인덱스 값은 각각 &quot;u&quot;, &quot;e&quot;, &quot;a&quot; 입니다. 이를 기준으로 strings를 정렬하면 [&quot;car&quot;, &quot;bed&quot;, &quot;sun&quot;] 입니다.</p>
<p>[입출력 예 2]
&quot;abce&quot;와 &quot;abcd&quot;, &quot;cdx&quot;의 2번째 인덱스 값은 &quot;c&quot;, &quot;c&quot;, &quot;x&quot;입니다. 따라서 정렬 후에는 &quot;cdx&quot;가 가장 뒤에 위치합니다. &quot;abce&quot;와 &quot;abcd&quot;는 사전순으로 정렬하면 &quot;abcd&quot;가 우선하므로, 답은 [&quot;abcd&quot;, &quot;abce&quot;, &quot;cdx&quot;] 입니다.</p>
<h3 id="나의-풀이">나의 풀이</h3>
<hr>
<p>**[시도 1] **</p>
<pre><code class="language-python">def solution(strings, n):
    answer = []
    # 1. 위치 바꾸기
    str_list = [list(word) for word in strings]
    for w in str_list:
        w.insert(0, w.pop(n))

    # 2. 정렬하기
    str_list.sort()

    # 3. 제자리 돌려놓기
    for w in str_list:
        w.insert(n, w.pop(0))
        answer.append(&#39;&#39;.join(w))

    return answer</code></pre>
<p> 나는 크게 3단계로 나누어서 풀었다.</p>
<ol>
<li>각 단어의 n번째 인덱스를 기준으로 정렬해야 하므로 각 리스트 안의 문자열을 리스트로 만든 후 <code>.insert()</code>와 <code>pop()</code>을 이용해 n번째 인덱스에 있는 문자열을 가장 앞(0번 인덱스)으로 가져온다.</li>
<li>바뀐 문자열 리스트를 기준으로 정렬을 한다.</li>
<li>단어의 순서를 정렬 후 원래의 단어로 바꾸기 위해 다시 <code>.insert()</code>와 <code>pop()</code>을 사용하고 다시 문자열로 </li>
</ol>
<p> =&gt; 처음 풀 때는 n번 인덱스의 문자열만 가장 앞으로 가져와야 하는데, 0번 인덱스와 n번 인덱스 문자열을 서로 바뀌게 해서 정렬이 제대로 되지 않아 조금 헤맸다...🥲</p>
<h3 id="다른-풀이">다른 풀이</h3>
<hr>
<pre><code class="language-python">def strange_sort(strings, n):
    return sorted(strings, key=lambda x: x[n])</code></pre>
<p><strong>[특정 키를 기준으로 정렬]</strong></p>
<ul>
<li><code>sort()</code>, <code>sorted()</code> 에 있는 key 변수를 사용한다. </li>
<li>key 인자에 <code>lambda</code> 함수를 넘겨 각 요소의 함수 반환값에 해당하는 값을 비교해 정렬되도록 설정한다.</li>
</ul>
<p>=&gt;  지금까지 쓴 적이 없어서 모르고 있었는데 당황스럽게도 아주 간단한 방법이 있었다😭 </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스]코딩테스트 연습 | 3진법 뒤집기]]></title>
            <link>https://velog.io/@haeng_9/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%97%B0%EC%8A%B5-3%EC%A7%84%EB%B2%95-%EB%92%A4%EC%A7%91%EA%B8%B0</link>
            <guid>https://velog.io/@haeng_9/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%97%B0%EC%8A%B5-3%EC%A7%84%EB%B2%95-%EB%92%A4%EC%A7%91%EA%B8%B0</guid>
            <pubDate>Tue, 22 Aug 2023 02:10:27 GMT</pubDate>
            <description><![CDATA[<h3 id="📜문제--3진법-뒤집기">📜문제 | 3진법 뒤집기</h3>
<blockquote>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/68935">https://school.programmers.co.kr/learn/courses/30/lessons/68935</a></p>
</blockquote>
<p>자연수 n이 매개변수로 주어집니다. n을 3진법 상에서 앞뒤로 뒤집은 후, 이를 다시 10진법으로 표현한 수를 return 하도록 solution 함수를 완성해주세요.</p>
<h3 id="입출력-예시">입출력 예시</h3>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/aad1e177-ab5e-4f6a-a7e2-61219777443b/image.png" alt=""></p>
<h3 id="나의-풀이">나의 풀이</h3>
<pre><code class="language-python">def solution(n):
    n3 = &quot;&quot;
    while n // 3 != 0:
        n3 += str(n % 3)
        n //= 3
    n3 += str(n)

    answer = 0
    for i in range(len(n3)):
        answer += int(n3[i]) * (3**(len(n3)-1-i))
    return answer</code></pre>
<p> 종종 쓰이는 8진법이나 16진법이 아닌 3진법의 변환은 익숙하지 않아 10진법을 2진법으로 만드는 식으로 바꿨다. while문과 for문을 모두 사용해 그닥 깔끔하지는 않은 코드 같다. </p>
<p> while문을 사용한 결과는 문자열에 담기는데 순서가 이미 뒤집어진 3진법으로 나온다. 그리고 for문을 통해 3진법으로 만든 문자열을 10진법 숫자로 변환했다.</p>
<h3 id="다른-풀이">다른 풀이</h3>
<pre><code class="language-python">def solution(n):
    tmp = &#39;&#39;
    while n:
        tmp += str(n % 3)
        n = n // 3

    answer = int(tmp, 3)
    return answer</code></pre>
<p>-&gt; 
while문을 사용해 빈 문자열에 값을 추가하는 부분은 비슷했고, (차이가 있다면 위의 풀이는 while문의 조건을 n으로 해서 n의 값이 0이되면 False가 되도록했다.)
int(n, 3)을 사용하면 간단하게 3진법의 수를 10진법으로 변환할 수 있었다😂</p>
<p><strong>[ n진수 -&gt; 10진수 변환 ]</strong>   : <code>int(string, base)</code> </p>
<p>그 외의 다른 풀이도 찾아보았는데 while문과 for문을 둘 다 쓰는 사람이 많았다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Error] OMP: Error #15: Initializing libiomp5md.dll, but found libiomp5md.dll already initialized.]]></title>
            <link>https://velog.io/@haeng_9/Error-OMP-Error-15-Initializing-libiomp5md.dll-but-found-libiomp5md.dll-already-initialized</link>
            <guid>https://velog.io/@haeng_9/Error-OMP-Error-15-Initializing-libiomp5md.dll-but-found-libiomp5md.dll-already-initialized</guid>
            <pubDate>Thu, 17 Aug 2023 05:25:49 GMT</pubDate>
            <description><![CDATA[<h2 id="😥-에러-상황">😥 에러 상황</h2>
<p>YOLOv8를 사용해 카메라로 영상을 입력받아 Tracking 하는 실습 중 에러 발생.</p>
<p>처음에는 <code>modulenotfounderror: no module named &#39;lap&#39;</code> 해당 에러가 발생했으나 가상 환경 설정시 파이썬 버전을 3.11로 설정해서 생긴 오류로  3.9로 다시 세팅하니 해결 되었다.</p>
<p><strong>[코드]</strong></p>
<pre><code class="language-python">import cv2
from ultralytics import YOLO

model = YOLO(&#39;yolov8n.pt&#39;)

cap = cv2.VideoCapture(0)

while cap.isOpened():
    ret, frame = cap.read()

    if ret :
        results = model.track(frame, persist=True)
        frame = results[0].plot()

    cv2.imshow(&#39;tracking&#39;, frame)

    if cv2.waitKey(1) &amp; 0xFF == ord(&#39;q&#39;):
        break

cap.release()</code></pre>
<p>실행 중 이번에는 
<img src="https://velog.velcdn.com/images/haeng_9/post/83bc5004-0e11-4d1a-bba0-0275b19f2a36/image.png" alt=""></p>
<p><code>OMP: Error #15: Initializing libiomp5md.dll, but found libiomp5md.dll already initialized.
OMP: Hint This means that multiple copies of the OpenMP runtime have been linked into the program. That is dangerous, since it can degrade performance or cause incorrect results. The best thing to do is to ensure that only a single OpenMP runtime is linked into the process, e.g. by avoiding static linking of the OpenMP runtime in any library. As an unsafe, unsupported, undocumented workaround you can set the environment variable KMP_DUPLICATE_LIB_OK=TRUE to allow the program to continue to execute, but that may cause crashes or silently produce incorrect results. For more information, please see http://www.intel.com/software/products/support/.</code> 
이러한 에러가 발생했다.</p>
<h2 id="해결">해결</h2>
<p>설치된 라이브러리들 간에 충돌이 생겨 발생하는 에러이다.</p>
<p>아래의 코드를 추가한 뒤 실행하면 문제 없이 실행되는 것을 확인할 수 있다. </p>
<pre><code class="language-python">import os
os.environ[&#39;KMP_DUPLICATE_LIB_OK&#39;]=&#39;True&#39;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[프로그래머스]코딩테스트 기초 트레이닝 | 0떼기]]></title>
            <link>https://velog.io/@haeng_9/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EA%B8%B0%EC%B4%88-%ED%8A%B8%EB%A0%88%EC%9D%B4%EB%8B%9D-0%EB%96%BC%EA%B8%B0</link>
            <guid>https://velog.io/@haeng_9/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EA%B8%B0%EC%B4%88-%ED%8A%B8%EB%A0%88%EC%9D%B4%EB%8B%9D-0%EB%96%BC%EA%B8%B0</guid>
            <pubDate>Sun, 16 Jul 2023 09:48:05 GMT</pubDate>
            <description><![CDATA[<h3 id="문제">문제</h3>
<p>정수로 이루어진 문자열 n_str이 주어질 때, n_str의 가장 왼쪽에 처음으로 등장하는 0들을 뗀 문자열을 return하도록 solution 함수를 완성해주세요.</p>
<p><strong>제한 사항</strong>
2 ≤ n_str ≤ 10
n_str이 &quot;0&quot;으로만 이루어진 경우는 없습니다.</p>
<h3 id="입출력-예시">입출력 예시</h3>
<p><img src="https://velog.velcdn.com/images/haeng_9/post/cd1c3076-5dec-421c-873f-0db66de9da69/image.png" alt=""></p>
<h3 id="풀이-방법">풀이 방법</h3>
<p><strong>방법 1</strong></p>
<pre><code class="language-python">def solution(n_str):
    return str(int(n_str))</code></pre>
<p>생각보다 엄청 간단하게 풀 수 있었는데 복잡하게 생각하느라 for문, while문까지 생각했었다...😢
문자열을 <code>int()</code>를 사용해 정수형으로 바꾸면 앞에 0들이 사라진다. 
그리고 다시 <code>str()</code>을 써서 문자열로 return하면 된다.</p>
<p><strong>방법 2</strong></p>
<pre><code class="language-python">def solution(n_str):
    return n_str.lstrip(&#39;0&#39;)</code></pre>
<p><code>lstrip()</code>은 왼쪽 공백만 제거한다고 생각했는데 인자값으로 문자를 넣으면 해당 문자를 string 왼쪽에서 제거할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[백준] 1920 수 찾기 ]]></title>
            <link>https://velog.io/@haeng_9/%EB%B0%B1%EC%A4%80-1920-%EC%88%98-%EC%B0%BE%EA%B8%B0</link>
            <guid>https://velog.io/@haeng_9/%EB%B0%B1%EC%A4%80-1920-%EC%88%98-%EC%B0%BE%EA%B8%B0</guid>
            <pubDate>Mon, 10 Jul 2023 05:54:36 GMT</pubDate>
            <description><![CDATA[<h3 id="문제--수-찾기">문제 | 수 찾기</h3>
<p>N개의 정수 A[1], A[2], …, A[N]이 주어져 있을 때, 이 안에 X라는 정수가 존재하는지 알아내는 프로그램을 작성하시오.</p>
<h3 id="입력">입력</h3>
<p>첫째 줄에 자연수 N(1 ≤ N ≤ 100,000)이 주어진다. 다음 줄에는 N개의 정수 A[1], A[2], …, A[N]이 주어진다. 다음 줄에는 M(1 ≤ M ≤ 100,000)이 주어진다. 다음 줄에는 M개의 수들이 주어지는데, 이 수들이 A안에 존재하는지 알아내면 된다. 모든 정수의 범위는 -231 보다 크거나 같고 231보다 작다.</p>
<h3 id="출력">출력</h3>
<p>M개의 줄에 답을 출력한다. 존재하면 1을, 존재하지 않으면 0을 출력한다.</p>
<hr>
<h2 id="제출-코드">제출 코드</h2>
<p><strong>시도 1</strong></p>
<pre><code class="language-python">a = int(input())
n_list = map(int, input().split())
b = int(input())
m_list = map(int, input().split())

for i in range(b):
    if m_list[i] in n_list:
        print(1)
    else:
        print(0)</code></pre>
<p>-&gt; 시간 초과 </p>
<p><strong>시도 2</strong></p>
<pre><code class="language-python">int(input())
n_list = set(map(int, input().split()))
int(input())
m_list = list(map(int, input().split()))

for i in range(len(m_list)):
    print(1) if m_list[i] in n_list else print(0)</code></pre>
]]></description>
        </item>
    </channel>
</rss>