<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dan_.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Sun, 26 May 2024 06:45:14 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>dan_.log</title>
            <url>https://velog.velcdn.com/images/dan_/profile/3e5f090d-f8fb-4229-9ddc-ce06a56b9fd8/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. dan_.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dan_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[cookie 실습, 400 상태코드, https, http, httponly, JWT 유효기간]]></title>
            <link>https://velog.io/@dan_/cookie-%EC%8B%A4%EC%8A%B5-400-%EC%83%81%ED%83%9C%EC%BD%94%EB%93%9C-https-http-httponly-JWT-%EC%9C%A0%ED%9A%A8%EA%B8%B0%EA%B0%84</link>
            <guid>https://velog.io/@dan_/cookie-%EC%8B%A4%EC%8A%B5-400-%EC%83%81%ED%83%9C%EC%BD%94%EB%93%9C-https-http-httponly-JWT-%EC%9C%A0%ED%9A%A8%EA%B8%B0%EA%B0%84</guid>
            <pubDate>Sun, 26 May 2024 06:45:14 GMT</pubDate>
            <description><![CDATA[<h2 id="1-cookie-사용해보기">1. cookie 사용해보기</h2>
<p>npm cookie 중에서 cookie-parser 사용하기</p>
<p>request에서의 쿠키를 파싱할 때 필요함</p>
<p>그냥 request에서 오는 거 처리 안 할거면 필요없지만 보통 다 필요하니까, 미리 설치하기</p>
<p><a href="https://www.npmjs.com/package/cookie-parser">npm: cookie-parser</a></p>
<h3 id="11-cookie">1.1 cookie</h3>
<p>쿠기로 보내기</p>
<pre><code class="language-jsx">res.cookie(&#39;token&#39;, token)</code></pre>
<p><img src="https://velog.velcdn.com/images/dan_/post/92724e9f-d20f-416b-90ca-0fd410534e9c/image.png" alt=""></p>
<h3 id="12-401-403-404">1.2 401, 403, 404</h3>
<p>4XX은 클라이언트에서의 오류를 나타내는 상태 코드</p>
<ul>
<li><strong>401 Unauthorized</strong><ul>
<li>인증이 안된 상태. 사용자 로그인이 필요함</li>
</ul>
</li>
<li><strong>403 Forbidden</strong><ul>
<li>서버가 요청은 이해했으나, 승인을 거부한 상태.</li>
<li>주로 인증될 자격은 있으나 인가될 자격은 없는 경우(접근 권한 불충분)</li>
</ul>
</li>
<li><strong>404 Not Found</strong>:<ul>
<li>요청 리소스가 서버에 없는 경우에 발생함.</li>
</ul>
</li>
</ul>
<h2 id="2-http-https-httponly-설정">2. HTTP, HTTPS, HTTPonly, 설정</h2>
<h3 id="21-http-vs-https-간단-비교">2.1 HTTP vs HTTPS 간단 비교</h3>
<blockquote>
<p>보안</p>
</blockquote>
<p>HTTP는 plain text로 데이터 전송</p>
<p>HTTPS는 SSL, TLS를 이용해서 데이터 암호화를 한 후 전송</p>
<blockquote>
<p>포트 넘버(default)</p>
</blockquote>
<p>HTTP - 80</p>
<p>HTTPS - 443</p>
<blockquote>
<p>SEO, browser indication</p>
</blockquote>
<p>HTTP - 가끔 구글링하면 NOT SECURE이라며 사용자에게 사이트 이용을 경고하는 경우 발생하는데, 모든 브라우저가 HTTP보다 HTTPS를 신뢰하고 주로 사용하기 때문임</p>
<p>HTTPS - 선호됨, 자물쇠모양 아이콘으로 표시됨</p>
<h3 id="22-httponly">2.2 HTTPOnly</h3>
<p>프론트엔드가 아니라 API 호출만 가능하도록 허락하는 경우</p>
<p>왜? XSS(웹 브라우저 JS 접근 후 공격)을 막기 위해서</p>
<p>cookie 전송하면서 설정하면 됨</p>
<pre><code class="language-jsx">res.cookie(&#39;token&#39;, token, {
    httpyOnly: true
})</code></pre>
<h2 id="3-유효-기간-이슈어-설정">3. 유효 기간, 이슈어 설정</h2>
<pre><code class="language-jsx">// 로그인
router
    .route(&#39;/login&#39;)
    .post(
        [
            body(&#39;email&#39;).notEmpty().isEmail().withMessage(&#39;이메일 입력 필요&#39;),
            body(&#39;password&#39;).notEmpty().isString().withMessage(&#39;비밀번호 입력 필요&#39;),
            validate
        ]
        ,(req,res, next)=&gt;{
            const {email, password} = req.body
            let sql = &quot;SELECT * FROM users WHERE email = ?&quot;

            conn.query(sql, email, 
                function(err, results){
                    var loginUser = results[0]

                    if (loginUser &amp;&amp; loginUser.password== password){
                        const token = jwt.sign({email: loginUser.email, 
                            name: loginUser.name}, process.env.LOGIN_KEY, {
                                expiresIn : &#39;30m&#39;,
                                issuer : &#39;daeun&#39;
                            })

                        res.cookie(&#39;token&#39;, token, {
                            httpyOnly: true
                        })

                        console.log(token)

                        res.status(200).json({
                            message: `${loginUser.name}님 로그인되었습니다.`
                        })
                    } else {
                        res.status(403).json({
                            message: &quot;이메일 또는 비밀번호 정보가 틀렸습니다.&quot;
                        })
                    }
                }
            )</code></pre>
<p>JWT로 디코딩해보면, </p>
<p>{
&quot;email&quot;: &quot;<a href="mailto:ga@gmail.com">ga@gmail.com</a>&quot;,
&quot;name&quot;: &quot;gagaga&quot;,
&quot;iat&quot;: 1716705058,
&quot;exp&quot;: 1716706858,
&quot;iss&quot;: &quot;daeun&quot;
}</p>
<p>이런 식으로, iat(issuedat), exp(expiration), iss(issuer) 확인 가능</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[next(), 인증과 인가, 쿠키 vs 세션 vs JWT, .env 환경]]></title>
            <link>https://velog.io/@dan_/next-%EC%9D%B8%EC%A6%9D%EA%B3%BC-%EC%9D%B8%EA%B0%80-%EC%BF%A0%ED%82%A4-vs-%EC%84%B8%EC%85%98-vs-JWT-.env-%ED%99%98%EA%B2%BD</link>
            <guid>https://velog.io/@dan_/next-%EC%9D%B8%EC%A6%9D%EA%B3%BC-%EC%9D%B8%EA%B0%80-%EC%BF%A0%ED%82%A4-vs-%EC%84%B8%EC%85%98-vs-JWT-.env-%ED%99%98%EA%B2%BD</guid>
            <pubDate>Sun, 26 May 2024 06:43:55 GMT</pubDate>
            <description><![CDATA[<h2 id="1-next">1. next()</h2>
<h3 id="11-방법1---미들웨어x-→-함수-호출">1.1 방법1 - 미들웨어X → 함수 호출</h3>
<p>validate 변수를 생성해서 미들웨어로 사용하지 않고,</p>
<p>그냥 함수로 제작 후 각 api에서 호출하는 형태로 사용하면, 에러 발생하지 않음</p>
<ol>
<li>유효성 검사 함수 생성</li>
</ol>
<pre><code class="language-jsx">function validate(req, res){
    const err = validationResult(req)

    if(!err.isEmpty()){
        return res.status(400).json(err.array())
    }
}</code></pre>
<ol>
<li>함수 호출</li>
</ol>
<pre><code>.get([body(&#39;userId&#39;).notEmpty().isInt().withMessage(&#39;숫자입력필요&#39;)]
    ,(req,res)=&gt;{
        validate(req,res)
</code></pre><h3 id="12-방법2---미들웨어-사용--next로-유효성-검사-통과-후-해야할-일-알려주기">1.2 방법2 - 미들웨어 사용 + next()로 유효성 검사 통과 후 해야할 일 알려주기</h3>
<ol>
<li>매개변수로 next 추가</li>
<li>만약, 유효성 검사 통과 시, 즉 err가 없을 시, return next()</li>
</ol>
<pre><code class="language-jsx">const validate = (req, res, next)=&gt;{
    const err = validationResult(req)

    if(!err.isEmpty()){
        return res.status(400).json(err.array())
    } else {
        return next(); // 다음 할 일 하러 찾으러 가봐!
    }
}</code></pre>
<pre><code class="language-jsx">.get([body(&#39;userId&#39;).notEmpty().isInt().withMessage(&#39;숫자입력필요&#39;),validate]
    ,(req,res, next)=&gt;{
</code></pre>
<h2 id="2-인증과-인가">2. 인증과 인가</h2>
<blockquote>
<p>인증</p>
</blockquote>
<p>== 로그인</p>
<blockquote>
<p>인가</p>
</blockquote>
<p>인증이 되었다고 해도,권한에 따라서 접근 가능한 페이지가 다름</p>
<p>어디까지 권한 가능한지 확인하는 것이 인가</p>
<p>인증 이후 인가</p>
<h2 id="3-쿠키-세션-차이">3. 쿠키 세션 차이</h2>
<p>로그인 유지에 필요한 개념으로 쿠키와 세션이 있음!</p>
<h3 id="31-쿠키">3.1 쿠키</h3>
<p>사이트에 한번 로그인하면 일정 기간동안은 로그인이 유지됨</p>
<p>다른 페이지로 이동해도 로그인 정보가 유지됨</p>
<p>이걸 가능하게 하는 방법으로 쿠키가 있는 것!</p>
<p>포춘 쿠키를 생각하자!</p>
<p>포춘 쿠키 안에 메시지를 담은 종이가 있듯이, 쿠키에도 필요한 정보를 담겨있음</p>
<p>로그인을 하면, 서버가 로그인 정보를 담은 쿠키를 구워줌</p>
<p>새로운 페이지에 갈때 이 쿠키를 서버에게 전달하면, 서버가 확인 후, 처리를 함</p>
<blockquote>
<p>장점</p>
</blockquote>
<ol>
<li>서버 저장 공간 절약<ol>
<li>서버가 로그인 정보를 서버 저장공간에 저장하는 게 아니라서, 서버 저장 공간을 아낄 수 있다.</li>
</ol>
</li>
<li>stateless하다, 따라서 RESTful하다</li>
</ol>
<aside>
⭐ stateless?

</aside>

<p>서버-클라이언트 구조에서 서버가 클라이언트 상태를 가지고 있지 않는 거</p>
<blockquote>
<p>단점</p>
</blockquote>
<ol>
<li>보안에 취약하다<ol>
<li>누군가 사용자와 서버 사이의 쿠키를 가로채서 정보를 훔쳐볼 수 있음</li>
</ol>
</li>
</ol>
<h3 id="32-세션">3.2 세션</h3>
<p>쿠키의 단점(보안에 취약)을 극복하기 위해서 세션이 탄생함</p>
<p>로그인 시, 서버는 서버 공간에 금고를 만들어서 정보를 보관 후, </p>
<p>그 서버 공간에 접근할 수 있는 금고 번호를 사용자에게 줌</p>
<p>사용자와 서버는 번호만을 가지고 대화를 진행</p>
<blockquote>
<p>장점</p>
</blockquote>
<ol>
<li>보안<ol>
<li>금고에 정보를 보관하니, 보안이 쿠키에 비해서 비교적 좋다.</li>
</ol>
</li>
</ol>
<blockquote>
<p>단점</p>
</blockquote>
<ol>
<li>서버에 정보를 저장한다.<ol>
<li>서버에 정보를 보관하니, 서버 저장 공간을 사용하게 된다.</li>
</ol>
</li>
<li>서버에 저장하니, stateless하지 못하다.(not RESTful)</li>
</ol>
<h2 id="4-jwt">4. JWT</h2>
<h3 id="41-jwt-개념">4.1 JWT 개념</h3>
<p>쿠키도, 세션도 단점이 존재하니, JWT가 탄생하게 된다.</p>
<p>JSON WEB TOKEN의 약자</p>
<p>JSON 형태로 데이터를 안전하게 전송하기 위한 토큰(웹에서 사용)이다.</p>
<p>옛날옛적 사용되던 버스 토큰을 생각하면 이해하기 쉽다.</p>
<p>버스를 타기 위해서는 버스 토큰이라는 게 필요했고, 버스토큰은 학생/성인용으로 나누어져있었음</p>
<p>이와 비슷하게 JWT도, 서버에게 사용자가 자신이 인증된 사용자라는 걸 ‘증명’하기 위한 수단으로 사용됨</p>
<p>또한, 버스토큰이 학생/성인으로 구분된 것처럼, </p>
<p>JWT도 관리자/일반 유저 권한을 구분함</p>
<p>결국, 두 가지에 대한 토큰인 것</p>
<ol>
<li><p>(인증용) 입장 가능한 유저라는 것</p>
</li>
<li><p>(인가용) 관리자 권한, 일반 유저 권한인지 </p>
</li>
</ol>
<h3 id="42-장점">4.2 장점</h3>
<ol>
<li>암호화가 되어있기에, 보안에 강하다</li>
<li>HTTP 특징을 잘 따르기에 Stateless하다. (’서버가 상태를 저장하지 않음’)</li>
<li>서버 저장 공간 절약, 서버 부담 줄여줌</li>
</ol>
<h3 id="43-구조">4.3 구조</h3>
<p><a href="https://jwt.io/">JWT.IO</a></p>
<p><img src="https://velog.velcdn.com/images/dan_/post/8d58f27c-748e-4978-b594-7575a142f1a0/image.png" alt=""></p>
<p>JWT는 header, payroad, signature의 구조를 가짐</p>
<blockquote>
<p>헤더</p>
</blockquote>
<p>토큰 타입(jwt), 암호화 알고리즘 명시</p>
<blockquote>
<p>페이로드</p>
</blockquote>
<p>사용자 정보, 전달하는 데이터</p>
<p>데이터 각각의 key는 claim</p>
<p>→ 페이로드는 a set of claim임</p>
<blockquote>
<p>시그니처</p>
</blockquote>
<p>헤더와 페이로드가 변조되었는지 확인하는 역할</p>
<p>페이로드를 수정하면 시그니처도 자동으로 수정됨 </p>
<p>JWT는 헤더, 페이로드, 시그니처 이 세 파트를 각각 인코딩해서 생성됨</p>
<pre><code class="language-jsx">const token = base64urlEncoding(header) 
+ &#39;.&#39; + base64urlEncoding(payload) 
+ &#39;.&#39; + base64urlEncoding(signature)</code></pre>
<h3 id="44-인증-인가-절차">4.4 인증-인가 절차</h3>
<p><img src="https://velog.velcdn.com/images/dan_/post/cc034a8b-1328-4496-a341-4b66702e4c10/image.png" alt=""></p>
<blockquote>
<p>첫 요청</p>
</blockquote>
<ol>
<li>클라이언트 → 서버</li>
</ol>
<p>로그인 요청</p>
<p>post / login</p>
<p>body(username, password)</p>
<ol>
<li>서버</li>
</ol>
<p>내부 로직 확인</p>
<p>내부 로직 통과 → JWT 발행(언제 로그인했는지 == JWT 발행 시점)</p>
<ol>
<li>서버 → 클라이언트</li>
</ol>
<p>로그인 승인</p>
<p>다음 요청부터는 </p>
<p>JWT 들고 다녀!</p>
<blockquote>
<p>이후 요청</p>
</blockquote>
<ol>
<li>다음 요청(클라이언트 → 서버)</li>
</ol>
<p>HTTP header에 JWT 담고, 요청 </p>
<ol>
<li>서버</li>
</ol>
<p>JWT 시그니처를 내부 로직에서 확인</p>
<p>일치/불일 파악</p>
<ol>
<li>서버 → 클라이언트</li>
</ol>
<p>일치하면, 승인</p>
<p>불일치며면 거절</p>
<h2 id="5-jwt-실습">5. JWT 실습</h2>
<p><a href="https://www.npmjs.com/package/jsonwebtoken">npm: jsonwebtoken</a></p>
<p>JWT 생성하기</p>
<pre><code class="language-jsx">var jwt = require(&#39;jsonwebtoken&#39;); // 모듈 불러오기 
var token = jwt.sign({ foo: &#39;bar&#39; }, &#39;shhhhh&#39;); // 토큰 생성</code></pre>
<p>jwt.sign의 첫 매개변수는 <code>페이로드</code></p>
<p>두 번째 매개변수는 <code>시그니처</code></p>
<p>보통 페이로드와 시그니처에 대해서 지정하고, 헤더는 자동으로 설정됨</p>
<blockquote>
<p>기본 헤더(따로 커스텀하지 않으면 자동 설정됨)</p>
</blockquote>
<pre><code class="language-jsx">{
  &quot;alg&quot;: &quot;HS256&quot;, // 기본 알고리즘
  &quot;typ&quot;: &quot;JWT&quot;    // 기본 타입
}</code></pre>
<blockquote>
<p>결과값</p>
</blockquote>
<pre><code class="language-jsx">eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE3MTYzNDg3ODB9.sjGX_6SPBijdMC9-eV29JcTeOMBdLJ8me3-s3_2e14w</code></pre>
<blockquote>
<p>디코딩해보기</p>
</blockquote>
<ol>
<li>jwt 사이트에서</li>
</ol>
<p><img src="https://velog.velcdn.com/images/dan_/post/44b985d2-ab1c-4169-8de7-aeed2a8dd9bd/image.png" alt=""></p>
<p>verify signature의 secret 입력란에 코드에서 입력했던 비밀 키 입력하면 verify 됨</p>
<ol>
<li>vscode에서</li>
</ol>
<pre><code class="language-jsx">var jwt = require(&#39;jsonwebtoken&#39;); // 모듈 소환
var token = jwt.sign({foo:&#39;gyul&#39;}, &#39;gyul&#39;) //  토큰 생성 ( == 서명함)

//검증
//검증 성공? -&gt; 페이로드 값 읽을 수 있음
var decoded = jwt.verify(token, &#39;gyul&#39;);
console.log(decoded) //{ foo: &#39;gyul&#39;, iat: 1716349210 }
console.log(decoded.foo) // gyul</code></pre>
<p><strong>jwt.verify(token, secretOrPublicKey, [options, callback])</strong></p>
<p>jwt.verify 메서드에 첫 번째 매개변수로는 토큰, 두 번째 매개변수로는 지정했던 시크릿 암호키를 입력하면, 페이로드 확인 가능!</p>
<p>결과값: - { foo: &#39;gyul&#39;, iat: 1716349210 }</p>
<p>iat(issuedat의 약자)는 발생시간의 초단위</p>
<p>발행시간에 따라서, 같은 내용이라도 토큰값이 다름</p>
<h2 id="6-env">6. .env</h2>
<p>그런데, jwt 인코딩, 디코딩시에 문제점이 존재함</p>
<p>바로, 시그니처 키값이 그대로 드러난다는 것</p>
<p>깃헙에 올리거나 공유할 때, 키 값은 따로 빼놓고 관리해야 함</p>
<p>이럴때 필요한 게 .env 파일임</p>
<h3 id="61-env란">6.1 .env란?</h3>
<p>포트넘버, db 계정, 암호키 등 외부에 유출하면 안되는 주요한 환경 변수를 따로 관리하는 파일</p>
<p>파일확장자는 .env, 프로젝트 폴더 최상위에 존재해야 함</p>
<p>→ 프로젝트의 구조적 일관성을 유지</p>
<p>→  환경 변수의 로딩 경로 문제를 방지</p>
<p>→  환경 분리와 보안 관리가 용이</p>
<p>(+) 최상단에 위치하지 않으면 에러 발생</p>
<pre><code class="language-jsx">/Users/daeun/node_modules/jsonwebtoken/sign.js:107
    throw err;
    ^

Error: secretOrPrivateKey must have a value</code></pre>
<blockquote>
<p>형식</p>
</blockquote>
<p>변수명은 대문자, 스네이크 형식</p>
<p>주석은 #으로</p>
<pre><code class="language-jsx">//.env 파일

PRIVATE_KEY = &#39;gyul&#39; #JWT 암호키</code></pre>
<pre><code class="language-jsx">//jwt-demo.js 파일

var jwt = require(&#39;jsonwebtoken&#39;); 
var dotenv = require(&#39;dotenv&#39;);

dotenv.config();

var token = jwt.sign({foo:&#39;gyul&#39;}, process.env.privateKey) 

var decoded = jwt.verify(token, process.env.privateKey);
console.log(decoded)
console.log(decoded.foo) </code></pre>
<ol>
<li>dotenv로 .env 파일 불러옴</li>
<li>dotenv.config() 다 활용한다</li>
<li>실제 사용은 process.env.파일에 저장된 값들 중 사용할 키 값</li>
</ol>
<h2 id="7-youtube-project에-jwt-적용하기">7. youtube project에 jwt 적용하기</h2>
<blockquote>
<p>.env 파일</p>
</blockquote>
<pre><code class="language-bash">PRIVATE_KEY = &#39;gyul&#39; #JWT 암호키

PORT = 7777 # 포트넘버

LOGIN_KEY = &#39;login&#39; #로그인 키값</code></pre>
<blockquote>
<p>로그인</p>
</blockquote>
<pre><code class="language-jsx">// 로그인
router
    .route(&#39;/login&#39;)
    .post(
        [
            body(&#39;email&#39;).notEmpty().isEmail().withMessage(&#39;이메일 입력 필요&#39;),
            body(&#39;password&#39;).notEmpty().isString().withMessage(&#39;비밀번호 입력 필요&#39;),
            validate
        ]
        ,(req,res, next)=&gt;{
            const {email, password} = req.body
            let sql = &quot;SELECT * FROM users WHERE email = ?&quot;

            conn.query(sql, email, 
                function(err, results){
                    var loginUser = results[0]

                    if (loginUser &amp;&amp; loginUser.password== password){
                        const token = jwt.sign({email: loginUser.email, 
                            name: loginUser.name}, process.env.LOGIN_KEY)

                        //res.cookie()

                        res.status(200).json({
                            message: `${loginUser.name}님 로그인되었습니다.`,
                            token_message: token
                        })
                    } else {
                        res.status(404).json({
                            message: &quot;이메일 또는 비밀번호 정보가 틀렸습니다.&quot;
                        })
                    }
                }
            )
})</code></pre>
<ol>
<li>로그인 성공 시, JWT 생성하기</li>
</ol>
<pre><code class="language-jsx">const token = jwt.sign({email: loginUser.email, 
                            name: loginUser.name}, process.env.LOGIN_KEY)</code></pre>
<ol start="2">
<li>body에 동봉하기(쿠키는 다음 파트에서)</li>
</ol>
<pre><code>res.status(200).json({
    message: `${loginUser.name}님 로그인되었습니다.`,
    token_message: token
})</code></pre><blockquote>
<p>결과</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dan_/post/83ed787f-5cf3-40b6-8f0d-350e1f2d1774/image.png" alt=""></p>
<p>로그인 성공, token까지 출력됨</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[유효성 검사(express-validator), 미들웨어화]]></title>
            <link>https://velog.io/@dan_/%EC%9C%A0%ED%9A%A8%EC%84%B1-%EA%B2%80%EC%82%ACexpress-validator-%EB%AF%B8%EB%93%A4%EC%9B%A8%EC%96%B4%ED%99%94</link>
            <guid>https://velog.io/@dan_/%EC%9C%A0%ED%9A%A8%EC%84%B1-%EA%B2%80%EC%82%ACexpress-validator-%EB%AF%B8%EB%93%A4%EC%9B%A8%EC%96%B4%ED%99%94</guid>
            <pubDate>Sun, 26 May 2024 06:39:57 GMT</pubDate>
            <description><![CDATA[<h2 id="1-유효성-검사">1. 유효성 검사</h2>
<h3 id="11-express-validator">1.1 express-validator</h3>
<p>사용자가 입력한 값의 유효성(타당성)을 확인하는 것</p>
<p>예를 들어서, 우리 채널 post의 user_id, name의 경우</p>
<p>전자는 값이 있고 숫자여야 하고, 후자는 숫자가 아닌 문자열이며 2자리 이상이여야 함</p>
<p>이를 위해서는 <code>express-validator</code>가 필요함</p>
<blockquote>
<p>express-validator 설치</p>
</blockquote>
<p><a href="https://express-validator.github.io/docs/guides/getting-started">Getting Started | express-validator</a></p>
<pre><code class="language-jsx">npm install express-validator</code></pre>
<h3 id="12-express-validator-사용">1.2 express-validator 사용</h3>
<pre><code class="language-jsx">const express = require(&#39;express&#39;);
const router = express.Router();
const conn = require(&#39;./mariadb&#39;)
const {body, validationResult} = require(&#39;express-validator&#39;)

router.use(express.json());

router
    .route(&#39;/&#39;)
    // 채널 생성
    .post(
        [body(&#39;userId&#39;).notEmpty().isInt().withMessage(&#39;숫자입력필요&#39;), 
        body(&#39;channelName&#39;).notEmpty().isString().withMessage(&#39;문자입력필요&#39;)]
        , (req,res)=&gt;{
            const err = validationResult(req)

            if(!err.isEmpty()){
                return res.status(400).json(err.array())
            }

            const {channelName, userId} = req.body
            let sql = &#39;INSERT INTO channels (name, user_id) VALUES (?, ?)&#39;
            let values = [channelName, userId]

            conn.query(sql, values,
                function(err, results){
                    res.status(201).json(results)
                }
            )

    })</code></pre>
<blockquote>
<p>const {body, validationResult} = require(&#39;express-validator&#39;)</p>
</blockquote>
<p>express-validator 모듈로부터 body와 validationResult 변수 가져오기</p>
<blockquote>
<p>[body(&#39;userId&#39;).notEmpty().isInt().withMessage(&#39;숫자입력필요&#39;), 
 body(&#39;channelName&#39;).notEmpty().isString().withMessage(&#39;문자입력필요&#39;)]</p>
</blockquote>
<p>req.body의 userId는 값이 있고, 숫자여야 하고,</p>
<p>req.body의 channelName은 값이 있고, 문자열이여야 함</p>
<p>위의 코드는 userId와 channelName의 필드를 검사하는 것</p>
<p>만약 유효성 검사가 실패한다면, withMessage의 메시지가 반환됨</p>
<blockquote>
<p>const err = validationResult(req)</p>
</blockquote>
<p>요청의 유효성 검사 결과를 err 변수에 할당한 것</p>
<p>유효성 검사가 성공하면 err.isEmpty()는 true를, 실패하면 false를 반환함</p>
<blockquote>
<p>if(!err.isEmpty()){
    return res.status(400).json(err.array())
}</p>
</blockquote>
<p>err 변수(유효성 검사 결과)가 실패하면, 404 반환</p>
<p>return을 사용하여 코드가 종료되도록 설정</p>
<blockquote>
<p>const {channelName, userId} = req.body
let sql = &#39;INSERT INTO channels (name, user_id) VALUES (?, ?)&#39;
let values = [channelName, userId]</p>
</blockquote>
<p>conn.query(sql, values,
        function(err, results){
           res.status(201).json(results)
        }
)</p>
<blockquote>
</blockquote>
<p>유효성 검사 통과한 경우 실행될 코드들</p>
<h3 id="13-sql-err-처리">1.3 sql err 처리</h3>
<pre><code>conn.query(sql, values,
    function(err, results){
        if(err){
            return res.status(400).end();
        }
        res.status(201).json(results)
    }
)</code></pre><p>SQL에서 에러가 발생하는 경우에 예외 처리하기</p>
<p>예를 들어서, userId는 FK인데, 입력값이 FK 중에 없는 경우</p>
<blockquote>
<p>SQL 에러와, 유효성 검사 에러 차이</p>
</blockquote>
<p>유효성 검사 에러의 경우는 빈 값인지, 입력값의 타입이 무엇인지 확인 후, 설정값과 다르면 에러를 발생하는 것이고</p>
<p>SQL 에러의 경우는, 유효성 검사를 통과해도, 실질적으로 db 상에서 없는 값일 경우에 에리가 발생되는 것임</p>
<h2 id="2-채널-개별-조회">2. 채널 개별 조회</h2>
<pre><code class="language-jsx">const {body, param, validationResult} = require(&#39;express-validator&#39;)

.get(param(&#39;id&#39;).notEmpty().withMessage(&#39;아이디 입력 필요&#39;)
,(req,res)=&gt;{
    const err = validationResult(req)

    if(!err.isEmpty()){
        res.status(400).end();
    }

    let {id} = req.params
    id = parseInt(id)

    let sql = &#39;SELECT * FROM channels WHERE id =?&#39;

    conn.query(sql, id, 
        function(err, results){
            if(results.length){
                res.status(200).json(results)
            } else {
                notFoundChannel(res)
            }
    })
})</code></pre>
<blockquote>
<p>const {body, param, validationResult} = require(&#39;express-validator&#39;)</p>
</blockquote>
<p>param도 유효성 검사에 추가하기</p>
<blockquote>
<p>notEmpty()</p>
</blockquote>
<p>req.body의 userId는 값이 있고, 숫자여야 함</p>
<h2 id="3-채널-업데이트">3. 채널 업데이트</h2>
<pre><code class="language-jsx">router 
    .route(&#39;/:id&#39;)
    //채널 수정
    .put(
        [param(&#39;id&#39;).notEmpty().withMessage(&#39;아이디 입력 필요&#39;),
        body(&#39;name&#39;).notEmpty().withMessage(&#39;아이디 입력 필요&#39;)]
        ,(req,res)=&gt;{
            const err = validationResult(req)

            if(!err.isEmpty()){
                res.status(400).end();
            }

            let {id} = req.params
            id = parseInt(id)
            let {name} = req.body
            let sql = &#39;UPDATE channels SET name = ? WHERE id = ?&#39;
            let values = [name, id]

            conn.query(sql, values, 
                function(err, results){
                    if(err || results.affectedRows == 0){ 
                        return res.status(400).end() 
                    } else {
                        res.status(200).json(results)
                    }
            })
        })</code></pre>
<blockquote>
<p>if(err || results.affectedRows == 0){</p>
</blockquote>
<p>예외 처리</p>
<ol>
<li>err일 경우</li>
<li>results.affectedRow가 0인 경우 (Id가 db에 없는 값이라면,,,)</li>
</ol>
<p>둘 중에 하나라도 해당되면, 400 띄우도록 세팅</p>
<h2 id="4-채널-삭제">4. 채널 삭제</h2>
<pre><code class="language-jsx">  router 
    .route(&#39;/:id&#39;)
    .delete(
        param(&#39;id&#39;).notEmpty().withMessage(&#39;아이디입력필요&#39;)
        ,(req,res)=&gt;{
            const err = validationResult(req)

            if(!err.isEmpty()){
                return res.status(400).json(err.array());
            }

            let {id} = req.params
            id = parseInt(id)
            let sql = &#39;DELETE FROM channels WHERE id = ?&#39;

            conn.query(sql, id, function(err,results){
                if(err || results.affectedRows == 0){
                    res.status(400).end()
                } else {
                    res.status(200).json(results)
                }
            })
    })</code></pre>
<h2 id="5-validate-미들웨어">5. validate 미들웨어</h2>
<pre><code class="language-jsx">    const err = validationResult(req)

    if(!err.isEmpty()){
        return res.status(400).json(err.array())
    }
}</code></pre>
<p>모든 채널 api에서 사용되는 유효성 검사 코드를</p>
<p>미들웨어로 생성해서 활용하기</p>
<h3 id="51-미들웨어-활용">5.1 미들웨어 활용</h3>
<ol>
<li>변수에 함수 할당</li>
</ol>
<pre><code class="language-jsx">const validate = (req, res)=&gt;{
    const err = validationResult(req)

    if(!err.isEmpty()){
        return res.status(400).json(err.array())
    }
}
</code></pre>
<ol>
<li>콜백 함수 이전 코드에 삽입</li>
</ol>
<pre><code class="language-jsx">.get([body(&#39;userId&#39;).notEmpty().isInt().withMessage(&#39;숫자입력필요&#39;)
, validate]
,(req,res)=&gt;{</code></pre>
<h3 id="52-문제점">5.2 문제점</h3>
<p>validate 함수를 통과해도 이후의 코드를 실행하지 않고</p>
<p>로딩만 진행됨</p>
<p>→ 해결해야 함</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[DB 모듈화, socket hang up?, 코드 리팩토링]]></title>
            <link>https://velog.io/@dan_/DB-%EB%AA%A8%EB%93%88%ED%99%94-socket-hang-up-%EC%BD%94%EB%93%9C-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81</link>
            <guid>https://velog.io/@dan_/DB-%EB%AA%A8%EB%93%88%ED%99%94-socket-hang-up-%EC%BD%94%EB%93%9C-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81</guid>
            <pubDate>Sun, 26 May 2024 06:39:09 GMT</pubDate>
            <description><![CDATA[<h2 id="1-db-모듈화-usersjs에서-사용해보기">1. db 모듈화, users.js에서 사용해보기</h2>
<h3 id="11-mariadbjs-생성-사용하기">1.1 mariadb.js 생성, 사용하기</h3>
<pre><code class="language-jsx">// Get the client
const mysql = require(&#39;mysql2&#39;);

// Create the connection to database
const connection = mysql.createConnection({
  host: &#39;localhost&#39;,
  user: &#39;root&#39;,
  password: &#39;root&#39;,
  database: &#39;Youtube&#39;,
  dateStrings : true
});

module.exports = connection;
</code></pre>
<blockquote>
<p>모듈화하기</p>
</blockquote>
<p>module.exports = connection;</p>
<h3 id="12-usersjs에서-사용하기">1.2 users.js에서 사용하기</h3>
<blockquote>
<p>connection 모듈 불러오기</p>
</blockquote>
<pre><code class="language-sql">const conn = require(&#39;../workbench/mariadb&#39;)</code></pre>
<blockquote>
<p>conn 쿼리문, .get(/users)에서 db 사용하기</p>
</blockquote>
<pre><code class="language-sql">router
    .route(&#39;/users&#39;)
    .get((req, res) =&gt; {
        let { email } = req.body;

        // 파라미터 바인딩을 사용하여 SQL 인젝션 방지
        conn.query(
            `SELECT * FROM user WHERE email = ${email}`,
            function (err, results, fields) {
                // if (err) {
                //     return res.status(500).send({ message: &#39;Database query error&#39; });
                // }
                if (results.length) {
                    res.status(200).json(results);
                } else {
                    res.status(400).send({
                        message: &#39;회원 정보가 없습니다.&#39;
                    });
                }
            }
        );
    })</code></pre>
<h2 id="2-socket-hang-up">2. socket hang up</h2>
<p>코드를 워크벤치에서 돌리면, “socket hang up”이라는 오류 발생함</p>
<pre><code class="language-bash">[Running] node &quot;/Users/daeun/Desktop/backend-basic/youtuber-project/app.js&quot;
/Users/daeun/Desktop/backend-basic/youtuber-project/routes/users.js:24
                if (results.length) {
                            ^

TypeError: Cannot read properties of undefined (reading &#39;length&#39;)
    at Query.onResult (/Users/daeun/Desktop/backend-basic/youtuber-project/routes/users.js:24:29)
    at Connection._notifyError (/Users/daeun/node_modules/mysql2/lib/connection.js:228:21)
    at Connection._handleFatalError (/Users/daeun/node_modules/mysql2/lib/connection.js:183:10)
    at Connection.handlePacket (/Users/daeun/node_modules/mysql2/lib/connection.js:491:12)
    at PacketParser.onPacket (/Users/daeun/node_modules/mysql2/lib/connection.js:97:12)
    at PacketParser.executeStart (/Users/daeun/node_modules/mysql2/lib/packet_parser.js:75:16)
    at Socket.&lt;anonymous&gt; (/Users/daeun/node_modules/mysql2/lib/connection.js:104:25)
    at Socket.emit (node:events:518:28)
    at addChunk (node:internal/streams/readable:559:12)
    at readableAddChunkPushByteMode (node:internal/streams/readable:510:3)

Node.js v20.12.2

[Done] exited with code=1 in 3.078 seconds</code></pre>
<p>results가 Undefined이고, undefined의 Length는 읽을 수 없어서, 오류가 발생한 것</p>
<blockquote>
<p>socket이란?</p>
</blockquote>
<blockquote>
<p>hang up</p>
</blockquote>
<p>서버가 죽음</p>
<blockquote>
<p>TypeError: Cannot read properties of undefined (reading &#39;length&#39;)</p>
</blockquote>
<p>템플릿 문자열</p>
<p>SQL에서 사용하는 정석 문자열 방법</p>
<p>물음표(?) 사용하기</p>
<p>query(sql, ? 변수, 콜백 함수)</p>
<pre><code class="language-jsx">router
    .route(&#39;/users&#39;)
    .get((req, res) =&gt; {
        let { email } = req.body;

        // 파라미터 바인딩을 사용하여 SQL 인젝션 방지
        conn.query(
            &quot;SELECT * FROM users WHERE email = ?&quot;, email,
            function (err, results, fields) {
                // if (err) {
                //     return res.status(500).send({ message: &#39;Database query error&#39; });
                // }
                console.log(email)
                console.log(results)
                if (results.length) {
                    res.status(200).json(results);
                } else {
                    res.status(400).send({
                        message: &#39;회원 정보가 없습니다.&#39;
                    });
                }
            }
        );
    })</code></pre>
<blockquote>
<p>? 자리표시자로 sql 작성하기</p>
</blockquote>
<p>&quot;SELECT * FROM users WHERE email = ?&quot;, email, function()</p>
<h2 id="3-회원가입-insert">3. 회원가입 INSERT</h2>
<pre><code class="language-jsx">// 회원가입
router.post(&#39;/join&#39;, (req,res)=&gt;{
    if (req.body == {}){
        res.status(400).send({
            message: &#39;다시 입력해주세요&#39;
        })
    } else {
        let { email, name, password, contact } = req.body

        conn.query(&#39;INSERT INTO users (email, name, password, contact) VALUES (?,?,?,?)&#39;, [email, name, password, contact],
        function(err, results, fields){
            res.status(201).json(results)
        })
}})</code></pre>
<blockquote>
<p>구조분해할당</p>
</blockquote>
<p>let { email, name, password, contact } = req.body</p>
<blockquote>
<p>sql, 변수</p>
</blockquote>
<p>&quot;INSERT INTO users (email, name, password, contact) VALUES (?,?,?,?))&quot;, [email, name, password, contact]</p>
<p>→ 여러 변수를 ? 자리표시자를 이용해서 입력할 때는 배열[] 안에 넣기</p>
<h2 id="4-회원-정보-지우기-delete">4. 회원 정보 지우기 DELETE</h2>
<pre><code class="language-bash">  router
    .route(&#39;/users&#39;)
    .delete((req,res)=&gt;{
        let {email} = req.body;

        conn.query(&quot;DELETE FROM users WHERE email = ?&quot;, email,
            function(err, results, fields){
                res.json({
                    message: `${email}님, 다음에 또 뵙겠습니다.`
             })
        })
    })</code></pre>
<h2 id="5-로그인-post">5. 로그인 POST</h2>
<h3 id="51-버전-1아이디-비밀번호-분리해서-알려주기">5.1 버전 1(아이디, 비밀번호 분리해서 알려주기)</h3>
<pre><code class="language-jsx">router.post(&#39;/login&#39;, (req,res)=&gt;{
    const {email, password} = req.body
    var loginUser = {}

    conn.query(&quot;SELECT * FROM users WHERE email = ?&quot;, email, 
        function(err, results, fields){
            var loginUser = results[0]
            if (results.length){
                if(loginUser.password == password){
                    res.status(200).json({
                        message: `${loginUser.name}님 로그인되었습니다.`
                    })
                } else {
                    res.status(400).json({
                        message: &quot;비밀번호가 틀렸습니다.&quot;
                    })
                }
            } else {
                res.status(404).json({
                    message: &quot;회원정보가 없습니다.&quot;
                })
            }
            }
        )
})
</code></pre>
<blockquote>
<p>loginUser = results[0]</p>
</blockquote>
<p>results가 배열 안에 있기에, 첫번째 인덱스를 loginUser에 넣어주기</p>
<h3 id="52-버전2아이디-비밀번호-통합해서-알려주기---요즘-트렌드">5.2 버전2(아이디, 비밀번호 통합해서 알려주기) - 요즘 트렌드</h3>
<pre><code class="language-jsx">router.post(&#39;/login&#39;, (req,res)=&gt;{
    const {email, password} = req.body
    var loginUser = {}

    conn.query(&quot;SELECT * FROM users WHERE email = ?&quot;, email, 
        function(err, results, fields){
            var loginUser = results[0]
            if (loginUser.length &amp;&amp; loginUser.password== password){
                    res.status(200).json({
                        message: `${loginUser.name}님 로그인되었습니다.`
                    })
            } else {
                res.status(404).json({
                    message: &quot;이메일 또는 비밀번호 정보가 틀렸습니다.&quot;
                })
            }
        }
    )
})</code></pre>
<h2 id="6-usersjs-리팩토링">6. users.js 리팩토링</h2>
<ul>
<li>안 쓰는 코드 제거 (코드로 설명 안되는 부분에만 주석이 달려야 함)</li>
<li>변수명</li>
<li>메소드명</li>
<li>주석 유무, 주석 내용</li>
</ul>
<h3 id="61-리팩토링-전">6.1 리팩토링 전</h3>
<pre><code class="language-jsx">const express = require(&#39;express&#39;);
const router = express.Router(); // users.js를 ezpress의 router로 사용가능
const conn = require(&#39;../workbench/mariadb&#39;)

router.use(express.json());

const db = new Map();
let num = 1;

// (추가) app.route 사용해보기
// .get과 .delete가 path가 동일하니까, route로 동일하게 합쳐서 사용 가능
router
    .route(&#39;/users&#39;)
    .get((req, res) =&gt; {
        let { email } = req.body;

        // 파라미터 바인딩을 사용하여 SQL 인젝션 방지
        conn.query(
            &quot;SELECT * FROM users WHERE email = ?&quot;, email,
            function (err, results, fields) {
                // if (err) {
                //     return res.status(500).send({ message: &#39;Database query error&#39; });
                // }
                console.log(email)
                console.log(results)
                if (results.length) {
                    res.status(200).json(results);
                } else {
                    res.status(400).send({
                        message: &#39;회원 정보가 없습니다.&#39;
                    });
                }
            }
        );
    })


    .delete((req,res)=&gt;{
        let {email} = req.body;

        conn.query(&quot;DELETE FROM users WHERE email = ?&quot;, email,
            function(err, results, fields){
                res.json({
                    message: `${email}님, 다음에 또 뵙겠습니다.`
             })
        })
    })

// 회원가입
router.post(&#39;/join&#39;, (req,res)=&gt;{
    if (req.body == {}){
        res.status(400).send({
            message: &#39;다시 입력해주세요&#39;
        })
    } else {
        let { email, name, password, contact } = req.body

        conn.query(&#39;INSERT INTO users (email, name, password, contact) VALUES (?,?,?,?)&#39;, [email, name, password, contact],
        function(err, results, fields){
            res.status(201).json(results)
        })
}})

// 로그인
// 아이디, 패스워드 각각 검사하는 코드
router.post(&#39;/login&#39;, (req,res)=&gt;{
    const {email, password} = req.body
    var loginUser = {}

    conn.query(&quot;SELECT * FROM users WHERE email = ?&quot;, email, 
        function(err, results, fields){
            var loginUser = results[0]
            if (loginUser.length &amp;&amp; loginUser.password== password){
                    res.status(200).json({
                        message: `${loginUser.name}님 로그인되었습니다.`
                    })
            } else {
                res.status(404).json({
                    message: &quot;이메일 또는 비밀번호 정보가 틀렸습니다.&quot;
                })
            }
        }
    )
})

module.exports = router</code></pre>
<h3 id="62-리팩토링-후">6.2 리팩토링 후</h3>
<pre><code class="language-jsx">const express = require(&#39;express&#39;);
const router = express.Router(); 
const conn = require(&#39;../workbench/mariadb&#39;)

router.use(express.json());

// 회원 개별 조회
// 회원 개별 삭제
router
    .route(&#39;/users&#39;)
    .get((req, res) =&gt; {
        let { email } = req.body;
        let sql = &quot;SELECT * FROM users WHERE email = ?&quot;

        conn.query(
            sql, email,
            function (err, results) {
                    res.status(200).json(results)
            }
        )
    })


    .delete((req,res)=&gt;{
        let {email} = req.body
        let sql = &quot;DELETE FROM users WHERE email = ?&quot;

        conn.query(sql, email,
            function(err, results){
                res.json(results)
        })
    })

// 회원가입
router.post(&#39;/join&#39;, (req,res)=&gt;{
    if (req.body == {}){
        res.status(400).send({
            message: &#39;다시 입력해주세요&#39;
        })
    } else {
        let { email, name, password, contact } = req.body
        let sql = &#39;INSERT INTO users (email, name, password, contact) VALUES (?,?,?,?)&#39;
        let values = [email, name, password, contact]

        conn.query(sql, values,
        function(err, results, fields){
            res.status(201).json(results)
        })
}})

// 로그인
router.post(&#39;/login&#39;, (req,res)=&gt;{
    const {email, password} = req.body
    let sql = &quot;SELECT * FROM users WHERE email = ?&quot;

    conn.query(sql, email, 
        function(err, results){
            var loginUser = results[0]

            if (loginUser &amp;&amp; loginUser.password== password){
                    res.status(200).json({
                        message: `${loginUser.name}님 로그인되었습니다.`
                    })
            } else {
                res.status(404).json({
                    message: &quot;이메일 또는 비밀번호 정보가 틀렸습니다.&quot;
                })
            }
        }
    )
})

module.exports = router</code></pre>
<blockquote>
<p>수정 사항</p>
</blockquote>
<ol>
<li>db 제거</li>
<li>query의 콜백함수의 fields 매개변수 사용하지 않으니 제거(err는 순서 때문에 사용하지 않더라도 유지해야 함)</li>
<li>필요없는 주석 제거</li>
<li>query 인수로 들어갈 값을 sql, values로 따로 빼서 변수로 담기</li>
</ol>
<h2 id="7-channelsjs-mariadb-사용-리팩토링">7. channels.js mariadb 사용, 리팩토링</h2>
<h3 id="71-수정-전-코드">7.1 수정 전 코드</h3>
<pre><code class="language-jsx">const express = require(&#39;express&#39;);
const router = express.Router();

router.use(express.json());

const db = new Map();
let id = 1;

router
    .route(&#39;/&#39;)
    // 채널 생성
    .post((req,res)=&gt;{
        let {channelTitle} = req.body

        if (channelTitle){
            db.set(id++, req.body)
            res.status(201).send({
                message: `${db.get(id-1).channelTitle} 채널을 응원합니다`
            })
            console.log(db)
        } else {
            res.status(400).send({
                message : `요청 값을 다시 확인해주세요.`
            })
        }
    })

    // 채널 전체 조회
    // third - if 고도화 후
    .get((req,res)=&gt;{
        var {userId} = req.body
        var channels = []
        if(db.size &amp;&amp; userId){
            db.forEach((x,y)=&gt;{
                if (x.userId == userId){
                    channels.push(x)
                }
            })
            if(channels.length){
                res.status(200).json(channels)
            } else {
                notFoundChannel()
        }} else {
            notFoundChannel()
        }
})

function notFoundChannel(){
    res.status(404).json({
        message: &quot;채널 정보를 찾을 수 없습니다.&quot;
    })
}

    // second - userId와 channel 연관성 고려 후, if 중첩 고도화 전
//     .get((req,res)=&gt;{
//         if(db.size){
//             var {userId} = req.body
//             var channels = []

//             if(userId == undefined){
//                 res.status(404).json({
//                      message: &quot;로그인이 필요한 페이지입니다.&quot;
//                 })
//             } else {
//                 db.forEach((x,y)=&gt;{
//                     if (x.userId == userId){
//                         channels.push(x)
//                     }
//                 })
//                 if(channels.length == 0){
//                     res.status(404).json({
//                         message: &#39;조회할 채널이 없습니다.&#39;
//                     })
//                 } else {
//                     res.status(200).json(channels)
//                 }
//             }
//         }}
// )

    // first - userId와 channel 연관성 고려 전
            // var channels = []

            // if(db.size){
            //     var channels = []
            //     db.forEach((ch, key)=&gt;{
            //         channels.push(ch)
            //     })

            //     res.status(200).json(channels)
            // } else {
            //     res.status(404).json({
            //         message: &#39;조회할 채널이 없습니다.&#39;
            //     })
            // }

    // })

router 
    .route(&#39;/:id&#39;)
    //채널 수정
    .put((req,res)=&gt;{
        let {id} = req.params
        id = parseInt(id)

        var channel = db.get(id)
        var oldTitle = channel.channelTitle

        let newTitle = req.body.channelTitle

        if(channel == undefined){
            res.status(400).send({
                message: `${id}가 존재하지 않습니다.`
            })
        } else {
            channel.channelTitle = newTitle
            db.set(id, channel)
            res.status(200).send({
                message: `${oldTitle}에서 ${newTitle}로 변경되었습니다`
            })
        }
    })
    // 채널 삭제
    .delete((req,res)=&gt;{
        let {id} = req.params
        id = parseInt(id)

        var channel = db.get(id)

        if(channel){
            db.delete(id)

            res.status(200).send({
                message:`${channel.channelTitle}가 삭제되었습니다.`
            })
        } else {
            res.status(400).send({
                message: `입력된 id ${id}가 데이터베이스에 없습니다.`
            })
        }
    })

    // 채널 개별 조회
    .get((req,res)=&gt;{
        let {id} = req.params
        id = parseInt(id)

        var ch = db.get(id)

        if(ch){
            res.status(200).json(ch)
        } else {
            res.status(404).send({
                message: &quot;다시 확인해주세요&quot;
            })
        }
    })

module.exports = router;
</code></pre>
<h3 id="72-채널-개별-조회">7.2 채널 개별 조회</h3>
<pre><code class="language-jsx">router 
    .route(&#39;/:id&#39;)
    .get((req,res)=&gt;{
        let {id} = req.params
        id = parseInt(id)
        let sql = &#39;SELECT * FROM channels WHERE id =?&#39;

        conn.query(sql, id, 
            function(err, results){
                if(results.length){
                    res.status(200).json(results)
                } else {
                    notFoundChannel(res)
                }
        })
    })</code></pre>
<h3 id="72-채널-전체-조회">7.2 채널 전체 조회</h3>
<pre><code class="language-jsx">router
    .route(&#39;/&#39;)
    .get((req,res)=&gt;{
        var {userId} = req.body
        let sql = &#39;SELECT * FROM channels WHERE user_id = ?&#39;

        if(userId){
            conn.query(sql, userId, function(err, results){
                if(results.length){
                    return res.status(200).json(results)
                } else {
                    return notFoundChannel(res)
                }
            }) 
        } else {
                return res.status(404).end();
            }
})</code></pre>
<blockquote>
<p>단축평가</p>
</blockquote>
<p>userId &amp;&amp; conn.query()</p>
<p>userId가 존재할 때, 쿼리 이하 부분 확인</p>
<blockquote>
<p>res.end()</p>
</blockquote>
<p>userId가 존재하지 않으면, 그냥 400 날리고 끝내기</p>
<h3 id="8-채널-생성">8. 채널 생성</h3>
<pre><code class="language-jsx">router
    .route(&#39;/&#39;)
    // 채널 생성
    .post((req,res)=&gt;{
        let {name, user_id} = req.body
        if(name, user_id){
            let {name, user_id} = req.body

            let sql = &#39;INSERT INTO channels (name, user_id) VALUES (?, ?)&#39;
            let values = [name, user_id]

            conn.query(sql, values,
                function(err, results){
                    res.status(201).json(results)
                }
            )
        } else {
            res.status(400).json({
                message: &quot;요청 값을 제대로 보내주세요.&quot;
            })
        }
    })</code></pre>
<aside>
⭐ 의도

</aside>

<p>name. user_id 가 req.body에 모두 담겨와야 하며, 각 타입도 문자열과 숫자여야 한다.</p>
<p>그런데 위의 코드의 경우에는 타입에 대한 확인이 되지 않아, user_id에 문자열이 담겨져도 201가 뜨게 되다. (db에는 insert 자동으로 걸러져서 업로드 되지 않지만, 위의 코드로는 오류가 발생하지 않는게 문제)</p>
<p>→ <code>유효성 검사가 필요하다!</code></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[데브코스][5-3] CLI로 DB 생성해보기, mysql 날짜 및 시간 타입 종류, JOIN]]></title>
            <link>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A45-3-CLI%EB%A1%9C-DB-%EC%83%9D%EC%84%B1%ED%95%B4%EB%B3%B4%EA%B8%B0-mysql-%EB%82%A0%EC%A7%9C-%EB%B0%8F-%EC%8B%9C%EA%B0%84-%ED%83%80%EC%9E%85-%EC%A2%85%EB%A5%98-JOIN</link>
            <guid>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A45-3-CLI%EB%A1%9C-DB-%EC%83%9D%EC%84%B1%ED%95%B4%EB%B3%B4%EA%B8%B0-mysql-%EB%82%A0%EC%A7%9C-%EB%B0%8F-%EC%8B%9C%EA%B0%84-%ED%83%80%EC%9E%85-%EC%A2%85%EB%A5%98-JOIN</guid>
            <pubDate>Mon, 13 May 2024 12:53:08 GMT</pubDate>
            <description><![CDATA[<h2 id="1-db-테이블-생성-실습">1. DB 테이블 생성 실습</h2>
<ol>
<li><p>“게시글” 스키마 생성</p>
<pre><code class="language-sql"> MariaDB [(none)]&gt; CREATE DATABASE Board;
 Query OK, 1 row affected (0.003 sec)

 MariaDB [(none)]&gt; USE Board;
 Database changed</code></pre>
</li>
<li><p>사용자 테이블 생성</p>
<pre><code class="language-sql"> CREATE TABLE users
 (
     id INT NOT NULL AUTO_INCREMENT,
     name VARCHAR (30) NOT NULL,
     job VARCHAR (100),
     birth DATE,
     PRIMARY KEY (id)
 );</code></pre>
<p> id는 숫자, 값이 없으면 안되니까 NOT NULL, 데이터 추가됨에 따라 자동으로 하나씩 오르도록 AUTO_INCREMENT </p>
<p> name은 글자, 값이 없으면 안되어서 NOT NULL</p>
<p> birth는 date 타입</p>
<p> PK는 id</p>
<blockquote>
<p>DESC 테이블; → 테이블 속성 확인</p>
</blockquote>
<pre><code class="language-sql"> //결과
 MariaDB [Board]&gt; DESC users;
 +-------+--------------+------+-----+---------+----------------+
 | Field | Type         | Null | Key | Default | Extra          |
 +-------+--------------+------+-----+---------+----------------+
 | id    | int(11)      | NO   | PRI | NULL    | auto_increment |
 | name  | varchar(30)  | NO   |     | NULL    |                |
 | job   | varchar(100) | YES  |     | NULL    |                |
 | birth | date         | YES  |     | NULL    |                |
 +-------+--------------+------+-----+---------+----------------+
 4 rows in set (0.010 sec)
</code></pre>
</li>
<li><p>사용자 데이터 삽입</p>
<pre><code class="language-sql"> //1
 INSERT INTO users (name, job, birth)
     VALUES (&quot;gongu&quot;, &quot;actor&quot;, &quot;800123&quot;);

 //2
 INSERT INTO users (name, job, birth)
     VALUES (&quot;gongu&quot;, &quot;actor&quot;, &quot;1980-01-23&quot;);</code></pre>
<p> 1번처럼 삽입해도 자동으로 birth 형태를 변경해줌</p>
<pre><code class="language-sql"> MariaDB [Board]&gt; INSERT INTO users (name, job, birth)
     -&gt; VALUES (&quot;gongu&quot;, &quot;actor&quot;, &quot;800123&quot;);
 Query OK, 1 row affected (0.010 sec)

 MariaDB [Board]&gt; SELECt * FROM users;
 +----+-------+-------+------------+
 | id | name  | job   | birth      |
 +----+-------+-------+------------+
 |  1 | gongu | actor | 1980-01-23 |
 +----+-------+-------+------------+
 1 row in set (0.004 sec)</code></pre>
<p> 하지만, 1900년대인지 2000년대인지 확실하게 구분하기 위해서는(더 나아가 다른 테이블에서도 연도 확실히 해야 하니까) 2번과 같이 풀형태에 맞게 넣는게 더 좋음</p>
</li>
<li><p>게시글 테이블 생성</p>
<pre><code class="language-sql"> CREATE TABLE posts
 (
     id INT NOT NULL AUTO_INCREMENT,
     title VARCHAR (100) NOT NULL,
     content VARCHAR (2000),
     created_at TIMESTAMP DEFAULT NOW(),
     PRIMARY KEY (id)
 );</code></pre>
<pre><code class="language-sql">
 MariaDB [Board]&gt; DESC posts;
 +------------+---------------+------+-----+---------------------+----------------+
 | Field      | Type          | Null | Key | Default             | Extra          |
 +------------+---------------+------+-----+---------------------+----------------+
 | id         | int(11)       | NO   | PRI | NULL                | auto_increment |
 | title      | varchar(100)  | NO   |     | NULL                |                |
 | content    | varchar(2000) | YES  |     | NULL                |                |
 | created_at | timestamp     | YES  |     | current_timestamp() |                |
 +------------+---------------+------+-----+---------------------+----------------+
 4 rows in set (0.006 sec)</code></pre>
</li>
<li><p>게시글 데이터 삽입</p>
<pre><code class="language-sql"> INSERT INTO posts (title, content)
     VALUES (&quot;title&quot;, &quot;content1&quot;);</code></pre>
<pre><code class="language-sql"> MariaDB [Board]&gt; SELECT * FROM posts;
 +----+-------+----------+---------------------+
 | id | title | content  | created_at          |
 +----+-------+----------+---------------------+
 |  1 | title | content1 | 2024-05-08 19:19:18 |
 +----+-------+----------+---------------------+
 1 row in set (0.002 sec)</code></pre>
</li>
<li><p>게시글 테이블에 수정일자 추가</p>
<p> 테이블 구조(테이블, 컬럼, 제약 조건 등)을 수정하는데에는 <code>ALTER</code>, 테이블의 데이터를 수정하는데는 <code>UPDATE</code></p>
<p> ALTER 이후 확인하려면 DESC</p>
<p> UPDATE 이후 확인하려면 SELECT</p>
<pre><code class="language-sql"> ALTER TABLE posts
 ADD COLUMN updated_at DATETIME
 DEFAULT NOW()
 ON UPDATE NOW();</code></pre>
</li>
</ol>
<ul>
<li><p>(+) 데이터 삽입</p>
<pre><code class="language-sql">  MariaDB [Board]&gt; INSERT INTO posts (title, content)
      -&gt; VALUES (&quot;title2&quot;, &quot;content2&quot;);</code></pre>
</li>
<li><p>삽입 결과</p>
<pre><code class="language-sql">  MariaDB [Board]&gt; SELECT * FROM posts;
  +----+--------+----------+---------------------+---------------------+
  | id | title  | content  | created_at          | updated_at          |
  +----+--------+----------+---------------------+---------------------+
  |  1 | title  | content1 | 2024-05-08 19:19:18 | 2024-05-08 19:24:57 |
  |  2 | title2 | content2 | 2024-05-08 19:27:13 | 2024-05-08 19:27:13 |
  +----+--------+----------+---------------------+---------------------+
  2 rows in set (0.001 sec)</code></pre>
</li>
</ul>
<ol>
<li><p>게시글 테이블 id 2의 content 수정</p>
<pre><code class="language-sql"> UPDATE posts
 SET content = &quot;updated!&quot;
 WHERE id = 2;</code></pre>
<ul>
<li><p>결과</p>
<p>```sql
MariaDB [Board]&gt; SELECT * FROM posts;</p>
</li>
<li><p>----+--------+----------+---------------------+---------------------+
| id | title  | content  | created_at          | updated_at          |</p>
</li>
<li><p>----+--------+----------+---------------------+---------------------+
|  1 | title  | content1 | 2024-05-08 19:19:18 | 2024-05-08 19:24:57 |
|  2 | title2 | updated! | 2024-05-08 19:27:13 | 2024-05-08 19:32:10 |</p>
</li>
<li><p>----+--------+----------+---------------------+---------------------+
2 rows in set (0.001 sec)</p>
<pre><code></code></pre></li>
</ul>
</li>
<li><p>게시글 테이블에 사용자 칼럼 FK 추가</p>
<pre><code class="language-sql"> ALTER TABLE posts
 ADD COLUMN user_id INT;

 ALTER TABLE posts
 ADD FOREIGN KEY(user_id)
 REFERENCES users(id);</code></pre>
<ul>
<li><p>결과</p>
<p>```sql
MariaDB [Board]&gt; DESC posts;</p>
</li>
<li><p>------------+---------------+------+-----+---------------------+-------------------------------+
| Field      | Type          | Null | Key | Default             | Extra                         |</p>
</li>
<li><p>------------+---------------+------+-----+---------------------+-------------------------------+
| id         | int(11)       | NO   | PRI | NULL                | auto_increment                |
| title      | varchar(100)  | NO   |     | NULL                |                               |
| content    | varchar(2000) | YES  |     | NULL                |                               |
| created_at | timestamp     | YES  |     | current_timestamp() |                               |
| updated_at | datetime      | YES  |     | current_timestamp() | on update current_timestamp() |
| user_id    | int(11)       | YES  | MUL | NULL                |                               |</p>
</li>
<li><p>------------+---------------+------+-----+---------------------+-------------------------------+</p>
<pre><code>
&lt;aside&gt;
⭐ MUL vs FK

&lt;/aside&gt;

FK인데 여러 키 값 중복해서 사용 가능하면 MUL</code></pre></li>
<li><p>user_id 추가해보기</p>
<p>```sql
MariaDB [Board]&gt; SELECT * FROM posts;</p>
</li>
<li><p>----+--------+----------+---------------------+---------------------+---------+
| id | title  | content  | created_at          | updated_at          | user_id |</p>
</li>
<li><p>----+--------+----------+---------------------+---------------------+---------+
|  1 | title  | content1 | 2024-05-08 19:19:18 | 2024-05-08 19:24:57 |    NULL |
|  2 | title2 | updated! | 2024-05-08 19:27:13 | 2024-05-08 19:32:10 |    NULL |
|  3 | title3 | content3 | 2024-05-08 19:57:56 | 2024-05-08 19:57:56 |    NULL |
|  6 | title3 | content3 | 2024-05-08 20:03:50 | 2024-05-08 20:03:50 |       1 |</p>
</li>
<li><p>----+--------+----------+---------------------+---------------------+---------+
4 rows in set (0.001 sec)</p>
<pre><code>
</code></pre></li>
</ul>
</li>
</ol>
<h2 id="cf-mysql-날짜-시간-타입">cf) MySQL 날짜, 시간 타입</h2>
<ol>
<li>DATE<ol>
<li>날짜만</li>
<li>YYYY-MM-DD</li>
</ol>
</li>
<li>DATETIME<ol>
<li>날짜 + 시간</li>
<li>YYYY-MM-DD HH:MM:SS(24시간제)</li>
</ol>
</li>
<li>TIME<ol>
<li>시간</li>
<li>HH:MM:SS</li>
</ol>
</li>
<li>TIMESTAMP <code>(자동 입력 기능)</code>(이 부분이 DATETIME과의 다른 점)<ol>
<li>날짜 + 시간</li>
<li>YYYY-MM-DD HH:MM:SS(24시간제)</li>
<li><code>Default → current_timestamp(), 시스템 시간대 정보에 맞게 일시를 저장함</code></li>
</ol>
</li>
</ol>
<h2 id="2-join">2. JOIN</h2>
<p>테이블 합치는 명령어</p>
<ul>
<li><p>JOIN 종류 4가지</p>
<ul>
<li><p>Inner join</p>
<ul>
<li><p>두 테이블 조인할 때, 두 테이블에 모두 지정한 열의 데이터가 존재해야 함</p>
<pre><code class="language-sql">SELECT &lt;열 목록&gt;
FROM &lt;첫 번째 테이블&gt;
  INNER JOIN &lt;두 번째 테이블&gt;
  ON &lt;조인 조건&gt;
[WHERE 검색 조건]
</code></pre>
</li>
</ul>
</li>
<li><p>outer join</p>
<ul>
<li><p>두 테이블 조인할 때, 1개의 테이블에만 데이터가 있어도 결과가 나옴</p>
</li>
<li><p>left outer join</p>
</li>
<li><p>right outer join</p>
</li>
<li><p>full outer join</p>
<pre><code class="language-sql">SELECT &lt;열 목록&gt;
FROM &lt;첫 번째 테이블(LEFT 테이블)&gt;
  &lt;LEFT | RIGHT | FULL&gt; OUTER JOIN &lt;두 번째 테이블(RIGHT 테이블)&gt;
   ON &lt;조인 조건&gt;
[WHERE 검색 조건]</code></pre>
</li>
</ul>
</li>
<li><p>cross join</p>
<ul>
<li><p>한쪽 테이블의 모든 행과 다른 쪽 테이블의 모든 행을 조인</p>
<pre><code class="language-sql">SELECT *
FROM &lt;첫 번째 테이블&gt;
  CROSS JOIN &lt;두 번째 테이블&gt;</code></pre>
</li>
</ul>
</li>
<li><p>self join</p>
<ul>
<li><p>자기 자신과 조인</p>
<pre><code class="language-sql">SELECT &lt;열 목록&gt;
FROM &lt;테이블&gt; 별칭A
  INNER JOIN &lt;테이블&gt; 별칭B
[WHERE 검색 조건]</code></pre>
<p><img src="https://prod-files-secure.s3.us-west-2.amazonaws.com/e67b413d-4bb2-4526-b1a4-a2aec7218637/55e4bb24-ec25-4f87-8df7-4465db5749b2/Untitled.png" alt="Untitled"></p>
<p><a href="https://hongong.hanbit.co.kr/sql-%EA%B8%B0%EB%B3%B8-%EB%AC%B8%EB%B2%95-joininner-outer-cross-self-join/">SQL 기본 문법: JOIN(INNER, OUTER, CROSS, SELF JOIN)</a></p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<ul>
<li>코드</li>
</ul>
<pre><code class="language-sql">SELECT * FROM posts LEFT 
JOIN users ON posts.user_id = users.id;</code></pre>
<ul>
<li>결과</li>
</ul>
<pre><code class="language-sql">MariaDB [Board]&gt; SELECT * FROM posts LEFT 
    -&gt; JOIN users ON posts.user_id = users.id;
+----+--------+----------+---------------------+---------------------+---------+------+-------+-------+------------+
| id | title  | content  | created_at          | updated_at          | user_id | id   | name  | job   | birth      |
+----+--------+----------+---------------------+---------------------+---------+------+-------+-------+------------+
|  1 | title  | content1 | 2024-05-08 19:19:18 | 2024-05-08 19:24:57 |    NULL | NULL | NULL  | NULL  | NULL       |
|  2 | title2 | updated! | 2024-05-08 19:27:13 | 2024-05-08 19:32:10 |    NULL | NULL | NULL  | NULL  | NULL       |
|  3 | title3 | content3 | 2024-05-08 19:57:56 | 2024-05-08 19:57:56 |    NULL | NULL | NULL  | NULL  | NULL       |
|  6 | title3 | content3 | 2024-05-08 20:03:50 | 2024-05-08 20:03:50 |       1 |    1 | gongu | actor | 1980-01-23 |
+----+--------+----------+---------------------+---------------------+---------+------+-------+-------+------------+</code></pre>
<p>데이터 삽입이 실패했어도 id가 계속 상승하는 모습을 볼 수 있음</p>
<p>→ AUTO_INCREMENT으로 설정한 것 때문임, 이 경우 디폴트 값을 AUTO_INCREMENT_LOCK_MODE로 하기</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[데브코스][5-2] DBMS, RDBMS, PK, FK, 테이블 정규화]]></title>
            <link>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A45-2-DBMS-RDBMS-PK-FK-%ED%85%8C%EC%9D%B4%EB%B8%94-%EC%A0%95%EA%B7%9C%ED%99%94</link>
            <guid>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A45-2-DBMS-RDBMS-PK-FK-%ED%85%8C%EC%9D%B4%EB%B8%94-%EC%A0%95%EA%B7%9C%ED%99%94</guid>
            <pubDate>Mon, 13 May 2024 12:50:51 GMT</pubDate>
            <description><![CDATA[<h2 id="1-데이터베이스란">1. 데이터베이스란?</h2>
<blockquote>
<p>데이터 베이스 정의</p>
</blockquote>
<p>데이터를 통합으로 효율적으로 관리하기 위한 데이터 집합체</p>
<p>데이터 구조화, 관리함으로써 데이터 중복 막고, 효율적이고 빠른 데이터 연산 가능하게 함</p>
<blockquote>
<p>데이터 베이스 장점?</p>
</blockquote>
<p>인터뷰에 잘 나오기 생각해보기</p>
<h3 id="11-dbms란">1.1 DBMS란?</h3>
<blockquote>
<p>DBMS 정의</p>
</blockquote>
<p>데이터베이스를 운영하고 관리하기 위한 DBMS를 통해서 사용하도록 함</p>
<blockquote>
<p>DBMS의 종류</p>
</blockquote>
<p>오라클, mysql, maraidb 등</p>
<p>부동의 1위는 오라클</p>
<blockquote>
<p>SQL</p>
</blockquote>
<p>DBMS 사용하기 위한 언어는 SQL, </p>
<p>CRUD 언어 익숙해지기</p>
<h2 id="2-rdbms-쓰는-이유">2. RDBMS 쓰는 이유?</h2>
<p>우리가 검색하고 생각하는 방법은</p>
<p>생각을 꼬리를 물고 진행됨</p>
<p>만약 배우 공유의 이름을 까먹어서 찾으려고 한다면</p>
<p>우리는 공유가 나온 드라마 “도깨비”라고 검색하고 도깨비의 등장 인물을 확인해서 찾을 것</p>
<p>우리 인간 세계의 검색 방법을 데이터베이스에 적용한 게 RDBMS</p>
<p>관계형 데이터 베이스</p>
<h2 id="3-pk-데이터-중복-정규화">3. PK, 데이터 중복, 정규화</h2>
<h3 id="31-pkprimary-key">3.1 PK(primary key)</h3>
<p>해당 테이블의 각 행을 유일하게 구별할 수 있는 키 값</p>
<p>각 행을 구별하기 위해서 필요함</p>
<h3 id="32-정규화">3.2 정규화</h3>
<table>
<thead>
<tr>
<th>id (PK)</th>
<th>title</th>
<th>content</th>
<th>author_name</th>
<th>occupation</th>
<th>birthdate</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>&quot;첫 번째 게시글&quot;</td>
<td>&quot;내용 내용 내용&quot;</td>
<td>&quot;홍길동&quot;</td>
<td>&quot;작가&quot;</td>
<td>1980-01-01</td>
</tr>
<tr>
<td>2</td>
<td>&quot;두 번째 게시글&quot;</td>
<td>&quot;내용 내용 내용&quot;</td>
<td>&quot;이몽룡&quot;</td>
<td>&quot;시인&quot;</td>
<td>1990-05-15</td>
</tr>
<tr>
<td>3</td>
<td>&quot;세 번째 게시글&quot;</td>
<td>&quot;내용 내용 내용&quot;</td>
<td>&quot;성춘향&quot;</td>
<td>&quot;소설가&quot;</td>
<td>1985-09-30</td>
</tr>
<tr>
<td>4</td>
<td>&quot;네 번째 게시글&quot;</td>
<td>&quot;내용 내용 내용&quot;</td>
<td>&quot;홍길동&quot;</td>
<td>&quot;작가&quot;</td>
<td>1980-01-01</td>
</tr>
<tr>
<td>5</td>
<td>&quot;다섯 번째 게시글&quot;</td>
<td>&quot;내용 내용 내용&quot;</td>
<td>&quot;이몽룡&quot;</td>
<td>&quot;시인&quot;</td>
<td>1985-12-25</td>
</tr>
</tbody></table>
<p>우리가 게시글 제작을 위한 테이블을 구성한다고 생각해보기</p>
<p>홍길동과 이몽룡의 경우 이름이 중복됨</p>
<p>이 사람들이 과연 동명이인인지 동일인물인지 알기 위해서 직업, 생년월일이 추가적으로 기입된 부분도 테이블에 추가한다면, 매번 직업, 생년월일 등이 게시글 테이블에 추가될 것</p>
<p>같은 인물이라면, 쓸데없이 데이터가 너무 많고 관리하기 어려움</p>
<p>따라서, 작성자와 작성자의 정보를 담은 테이블을 각기 만들기(정규화)</p>
<blockquote>
<p>정규화</p>
</blockquote>
<p>테이블을 쪼갠다는 뜻</p>
<p>데이터 중복을 없앤다</p>
<h2 id="4-테이블-분리-장-단점">4. 테이블 분리, 장-단점</h2>
<h3 id="41-테이블-분리시키기">4.1 테이블 분리시키기</h3>
<aside>
🗒️ 게시글 테이블

</aside>

<table>
<thead>
<tr>
<th>post_id (PK)</th>
<th>title</th>
<th>content</th>
<th>author_id (FK)</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>&quot;첫 번째 게시글&quot;</td>
<td>&quot;내용 내용 내용&quot;</td>
<td>1</td>
</tr>
<tr>
<td>2</td>
<td>&quot;두 번째 게시글&quot;</td>
<td>&quot;내용 내용 내용&quot;</td>
<td>2</td>
</tr>
<tr>
<td>3</td>
<td>&quot;세 번째 게시글&quot;</td>
<td>&quot;내용 내용 내용&quot;</td>
<td>4</td>
</tr>
<tr>
<td>4</td>
<td>&quot;네 번째 게시글&quot;</td>
<td>&quot;내용 내용 내용&quot;</td>
<td>1</td>
</tr>
<tr>
<td>5</td>
<td>&quot;다섯 번째 게시글&quot;</td>
<td>&quot;내용 내용 내용&quot;</td>
<td>3</td>
</tr>
</tbody></table>
<aside>
🗒️ 사용자 테이블

</aside>

<table>
<thead>
<tr>
<th>author_id (PK)</th>
<th>name</th>
<th>occupation</th>
<th>birthdate</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>&quot;홍길동&quot;</td>
<td>&quot;작가&quot;</td>
<td>1980-01-01</td>
</tr>
<tr>
<td>2</td>
<td>&quot;이몽룡&quot;</td>
<td>&quot;시인&quot;</td>
<td>1990-05-15</td>
</tr>
<tr>
<td>3</td>
<td>&quot;이몽룡&quot;</td>
<td>&quot;시인&quot;</td>
<td>1985-12-25</td>
</tr>
<tr>
<td>4</td>
<td>&quot;성춘향&quot;</td>
<td>&quot;소설가&quot;</td>
<td>1985-09-30</td>
</tr>
</tbody></table>
<h3 id="42">4.2</h3>
<p>장점: 분리해서 데이터 중복을 막는다</p>
<p>단점: 원하는 정보를 한 테이블에서 볼 수 없고 여러 테이블 이동해서 확인해야 한다 → 이건 이미 해결된 문제</p>
<h3 id="43-pk">4.3 PK</h3>
<p>사용자 테이블의 사용자 번호는 PK</p>
<p>게시글 테이블의 사용자 번호는 다른 테이블(사용자 테이블)의 PK이고, 이걸 게시글 테이블에서는 FK(foreign key) 역할을 시킨 것</p>
<p>A 테이블에서 B테이블의 데이터를 찾아가고 싶을 때, 사용하는 key 값</p>
<p>최대한 <code>B 테이블에서 PK 값을 A 테이블의 FK로 쓰는 것이 이상적</code>임</p>
<h2 id="5-생년월일-바꿔보기-1-n-관계의-주인">5. 생년월일 바꿔보기, 1-N, 관계의 주인</h2>
<p>데이터 베이스 테이블 간 어떤 관계를 가지고 있는지 <code>연관관계</code>는 3가지 종류 <code>1:1, 1:N, N:N</code></p>
<blockquote>
<p>게시글 vs 사용자의 경우 생각해보기</p>
</blockquote>
<p>1) 사용자 1명 → 게시글 여러 개 1:N</p>
<p>2) 게시글 1개 당 : 사용자 1명 1:1</p>
<p>게시글 테이블을 보고 사용자 확인 가능 : 연관관계</p>
<p>→ 게시글 테이블을 보고 사용자랑 어떤 관계인지</p>
<p>사용자를 보고는 게시글 테이터 확인 불가능</p>
<p>테이블 간의 연관관계를 확인해야 하면, 양쪽의 관계 모두 생각해보고</p>
<p>테이블 그려보고, 확인</p>
<p>cf) 현업에 계신 분들? 설계 어떻게 잘하나?</p>
<p>DBA분들이 도와주시거나, 하나씩 설계 후, 이걸 기준으로 구현하면, SQL 꼬이거나 너무 길다면 → 설계 뜯어고치기</p>
<h2 id="5-유튜브-실습">5. 유튜브 실습</h2>
<aside>
🗒️ 채널

</aside>

<table>
<thead>
<tr>
<th>채널 번호(PK)</th>
<th>채널명</th>
<th>구독자 수</th>
<th>영상 수</th>
<th>영상 수</th>
<th>채널주인</th>
<th>회원 id</th>
<th>비밀번호</th>
<th>연락처</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>스탠리</td>
<td>1</td>
<td>5</td>
<td>5</td>
<td>가가가</td>
<td>GA</td>
<td>1111</td>
<td>010-1249-4999</td>
</tr>
<tr>
<td>2</td>
<td>스타벅스</td>
<td>20</td>
<td>50</td>
<td>2</td>
<td>나나나</td>
<td>NA</td>
<td>2222</td>
<td>010-9149-4999</td>
</tr>
<tr>
<td>3</td>
<td>다이소</td>
<td>50</td>
<td>200</td>
<td>8</td>
<td>다다다</td>
<td>DA</td>
<td>3333</td>
<td>010-6549-4999</td>
</tr>
<tr>
<td>4</td>
<td>애플</td>
<td>1000</td>
<td>600</td>
<td>10</td>
<td>나나나</td>
<td>NA</td>
<td>2222</td>
<td>010-4249-4999</td>
</tr>
<tr>
<td>5</td>
<td>아마존</td>
<td>10000</td>
<td>900</td>
<td>132</td>
<td>마마마</td>
<td>MA</td>
<td>5555</td>
<td>010-3449-4999</td>
</tr>
</tbody></table>
<aside>
🗒️ 채널(분리)

</aside>

<table>
<thead>
<tr>
<th>채널 번호(PK)</th>
<th>채널명</th>
<th>구독자 수</th>
<th>영상 수</th>
<th>회원 id(FK)</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>스탠리</td>
<td>1</td>
<td>5</td>
<td>1</td>
</tr>
<tr>
<td>2</td>
<td>스타벅스</td>
<td>20</td>
<td>50</td>
<td>2</td>
</tr>
<tr>
<td>3</td>
<td>다이소</td>
<td>50</td>
<td>200</td>
<td>3</td>
</tr>
<tr>
<td>4</td>
<td>애플</td>
<td>1000</td>
<td>600</td>
<td>2</td>
</tr>
<tr>
<td>5</td>
<td>아마존</td>
<td>10000</td>
<td>900</td>
<td>4</td>
</tr>
</tbody></table>
<aside>
🗒️ 사용자(분리)

</aside>

<table>
<thead>
<tr>
<th>회원 id(PK)</th>
<th>채널주인</th>
<th>비밀번호</th>
<th>연락처</th>
</tr>
</thead>
<tbody><tr>
<td>GA</td>
<td>가가가</td>
<td>1111</td>
<td>010-1249-4999</td>
</tr>
<tr>
<td>NA</td>
<td>나나나</td>
<td>2222</td>
<td>010-9149-4999</td>
</tr>
<tr>
<td>DA</td>
<td>다다다</td>
<td>3333</td>
<td>010-6549-4999</td>
</tr>
<tr>
<td>MA</td>
<td>마마마</td>
<td>5555</td>
<td>010-3449-4999</td>
</tr>
</tbody></table>
<p>채널 vs 사용자</p>
<p>1) 사용자 1명 → 채널 n명 = 1:n</p>
<p>2) 채널 1개 → 사용자 1명 = 1:1</p>
<p>채널 테이블을 보고 사용자 확인 가능 : 연관관계</p>
<p>→ 게시글 테이블을 보고 사용자랑 어떤 관계인지</p>
<p>사용자를 보고는 채널 데이터 확인 불가능</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[데브코스][회고 - 4월] 데브코스를 시작하며...]]></title>
            <link>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4%ED%9A%8C%EA%B3%A0-4%EC%9B%94-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4%EB%A5%BC-%EC%8B%9C%EC%9E%91%ED%95%98%EB%A9%B0</link>
            <guid>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4%ED%9A%8C%EA%B3%A0-4%EC%9B%94-%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4%EB%A5%BC-%EC%8B%9C%EC%9E%91%ED%95%98%EB%A9%B0</guid>
            <pubDate>Tue, 30 Apr 2024 14:20:52 GMT</pubDate>
            <description><![CDATA[<h1 id="☁️-2024년-4월">☁️ 2024년 4월...</h1>
<p>회고는 처음 해본다. 
어색하지만, 여러 방법론 중에서 5F가 나랑 가장 잘 맞는 것 같아서 택했다.
다음엔 좀 더 개발 내용에 집중된 회고 방식을 사용해보고자 한다.</p>
<hr>
<p>시간이 정말 빠르게 지나가는 것 같다. 벌써 5월이라니,, 거의 1년의 절반의 시간이 흘렀다. 하루하루 밀도있게 살아야겠다는 생각이 강하게 드는 요즘이다!</p>
<h4 id="1-잘한-것은-무엇인가">1. 잘한 것은 무엇인가?</h4>
<p>a. 잘한 점은 데브코스를 신청했다는 것 자체가 아닐까싶다. 매일의 과제가 주어지고 그걸 해내가야만 한다는 의무가 주어진 것부터 나를 움직이게 해준다. 
b. 수업에서 깊게 다루지 않은 부분에 대해서 나름대로 찾아보고 공부한 점은 잘한 것 같다. 객체, express, req, res, params, query 등에 대해서 좀 더 깊게 찾아보려고 노력했다.
c. 수업 내용을 여러 번 복습했다. 깃허브 실습 같은 경우도 여러 번 더 진행해서 손에 익히도록 노력했다.</p>
<h4 id="2-잘못한-것은-무엇인가">2. 잘못한 것은 무엇인가?</h4>
<ol>
<li>수업을 제때 듣지 못한 점이 역시 가장 아쉬운 부분이다. 원래 스케줄에 맞게 계속 진행해야 의욕도 생기고, 주변 동료분들이랑 활달히 이야기도 가능한데, 하루씩 밀리니깐 의욕이나 자신감도 줄어드는 게 느껴진다. 주말에 힘을 내서 들어야겠다.</li>
<li>데브코스 이외에 하는 활동이 있어서 시간 조율이 어려운 것 같다. 시간 관리를 잘하고 좀 더 바쁘게 살아야 하는데, 체력이 부족해서 그런지 점점 의욕이 사라지는 것 같기도 하다ㅠㅠ</li>
<li>팀 활동이나 스터디에 제대로 참여하지 못했다. </li>
</ol>
<h4 id="3-무엇을-배웠는가">3. 무엇을 배웠는가?</h4>
<ol>
<li>백엔드에 대해서 전혀 몰랐는데, http, express에 대해서 배울 수 있었다.</li>
<li>데브코스 팀원분들은 정말 의욕적이고 에너지 넘치는 것 같다. 많이 부족한 팀원이라서 바쁘게 따라가야겠다는 생각이 들었다.</li>
<li>깃허브는 그동안 제대로 공부해야지 생각만 하고 제대로 공부한 적이 없었는데, 깃허브 개념부터 CLI로 PR하고 branch로 작업하는 것들을 배우니 정말 좋았다. 강사님께서 정말 친절하고 재밌게 설명해주셔서 이해가 정말 쉬웠다.</li>
</ol>
<h4 id="4-해결해야할-문제--남아있는-의문은-무엇인가">4. 해결해야할 문제 / 남아있는 의문은 무엇인가?</h4>
<ol>
<li>아직 제대로 깊게 찾아보지 못한 부분도 존재한다. (params, query 좀 더 깊은 내용)</li>
<li>스케줄에 맞게 수업을 듣지 못하고 있다.</li>
<li>코테 공부를 해야한다.</li>
<li>그동안 공부하던 JS 공부가 백엔드 공부로 소홀해지는 것 같다. </li>
</ol>
<h4 id="5-어떻게-개선할-것인가">5. 어떻게 개선할 것인가?</h4>
<ol>
<li>아무래도 하루 수강 시간이 10시간으로 제한되어있다보니, 평일엔 힘들고 주말에 밀린 부분을 마저 수강해야겠다.</li>
<li>프로그래머스 코테 문제를 하루에 3개씩 푸는 습관을 지속해야겠다. 꾸준히 하다가도 시간이 부족하면 항상 미뤄지는 게 아쉬웠다.</li>
<li>하루에 꼭 JS 공부 시간은 남겨두어야겠다. JS 내용 정리도 다시 진행해야겠다.</li>
<li>체력이 부족한 것 같아서, 운동을 좀 더 해야겠다. 운동 주기가 점점 길어지고 있었는데, 격일에 1번은 운동해야겠다. </li>
<li>팀원분들과 더 긴밀하게 이야기할 수 있도록 적극 참여해야겠다. 정말루,,,</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[데브코스][3-5] map, express-generator]]></title>
            <link>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A43-5-express-generator</link>
            <guid>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A43-5-express-generator</guid>
            <pubDate>Mon, 29 Apr 2024 14:57:52 GMT</pubDate>
            <description><![CDATA[<h2 id="1-map-object">1. map object</h2>
<pre><code class="language-jsx">let db = new Map();

let notebook = {
    productName : &#39;Notebook&#39;,
    price: 200000
}

let cup = {
    productName : &#39;Cup&#39;,
    price: 1000000
}

let chair = {
    productName : &#39;Chair&#39;,
    price: 3000000
}

let poster = {
    productName : &#39;poster&#39;,
    price: 5000000
}

db.set(1, notebook) 
db.set(2, cup);
db.set(3, chair);
db.set(4, poster);
</code></pre>
<p>db value로 객체 저장</p>
<h2 id="2-express--객체">2. express + 객체</h2>
<pre><code class="language-jsx">const express = require(&#39;express&#39;);
const app = express();

app.listen(3000);

// data setting
const youtuber1 = {
    channelTitle : &#39;MBCnews&#39;,
    sub : &#39;434만명&#39;,
    videoNum : &#39;24만개&#39;

}

const youtuber2 = {
    channelTitle : &#39;essential&#39;,
    sub : &#39;137만명&#39;,
    videoNum : &#39;391개&#39;

}

const youtuber3 = {
    channelTitle : &#39;뉴진스&#39;,
    sub : &#39;600만명&#39;,
    videoNum : &#39;500개&#39;

}

const db = new Map();

db.set(1, youtuber1)
db.set(2, youtuber2)
db.set(3, youtuber3)

console.log(db)

//REST API 설계
app.get(&#39;/youtuber/:id&#39;, function(req, res){
    let {id} = req.params

    id = parseInt(id)
    const youtuber = db.get(id)

    if (db.get(id) == undefined){
        res.json({
            message: &#39;유튜버 정보를 찾을 수 없습니다&#39;
        })
    } else {
        res.json(youtuber)}
})</code></pre>
<ol>
<li>db에 key, value(객체) 추가</li>
<li>youtuber 이하의 id 부분을 동적 변수로 한 url가 1번째 매개변수</li>
<li>구조분해할당으로 id 변수 생성</li>
<li>id 변수 숫자로 변경</li>
<li>youtuber 변수에 db에서 id 키값으로 얻은 value 할당</li>
<li>if문으로, db에 id가 있으면, youtuber(value) 값 띄우고 없으면 없다는 메시지 전달</li>
</ol>
<h2 id="3-express-구조-이해하기">3. express 구조 이해하기</h2>
<p>Expresss는 웹 프레임워크</p>
<p>웹 프레임워크 = 내가 만들고 싶은 웹 서비스를 구현하는데 필요한 모든 일을 틀 안에서 할 수 있는 것</p>
<h3 id="31-express-generator">3.1 express generator</h3>
<p>application 구조를 다운받기 위해서 express-generator를 사용함</p>
<p>express는 node.js에서 웹프로젝트를 간편하게 만들기 위해서 생성된 프레임워크</p>
<p>express-generator는 웹프로젝트 구조(MVC)를 간편하게 생성해주는 도구</p>
<p>express generator 실행하기</p>
<p><a href="https://expressjs.com/en/starter/generator.html">Express application generator</a></p>
<pre><code class="language-jsx">npx express-generator</code></pre>
<h2 id="4-js-4가지-함수">4. JS 4가지 함수</h2>
<pre><code class="language-jsx">function add1(x,y){
    return x + y;
}

let add2 = function(x,y){
    return x + y
}

//화살표 함수 1
const add3 = (x,y) =&gt; {
    return x + y;
}

//화살표 함수 2
var add4 = (x,y)=&gt; x + y

console.log(add1(1,2))
console.log(add2(1,2))
console.log(add3(1,2))
console.log(add4(1,2))</code></pre>
<p>함수 생성법으로는 리터럴, 선언문, 표현식, 메소드, 화살표함수 등등 존재</p>
<p>화살표 함수는 ⇒ 라는 독특한 방법으로 함수 생성, function 대신에 사용하는 것</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[데브코스][3-4] res.query]]></title>
            <link>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A43-4-res.query</link>
            <guid>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A43-4-res.query</guid>
            <pubDate>Sat, 27 Apr 2024 14:58:04 GMT</pubDate>
            <description><![CDATA[<h2 id="1-타입변환-parseint">1. 타입변환, parseInt</h2>
<pre><code class="language-jsx">const express = require(&#39;express&#39;)
const app = express()
// express command+클릭하면 파일 확인 가능

app.listen(3000)

app.get(&#39;/products/:n&#39;, function(req,res){

    if (req.params.n &gt; 10){
        console.log(&#39;url로 전달받은 숫자가 10보다 크네요.&#39;)
    }
    res.json({
        num: req.params.n
    })
})
</code></pre>
<p>이렇게, req.params.n이 숫자 10보다 큰 경우, 콘솔에 url로 전달받은 숫자가 10보다 크네요.라는 문구를 출력하도록 코딩해보자.</p>
<p><img src="https://velog.velcdn.com/images/dan_/post/4dded2fd-57a7-4d66-b9c4-d6219101fc29/image.png" alt=""></p>
<p>req.params.n은 “11”로 문자열인데, 콘솔에는 문구가 출력됨</p>
<p><img src="https://velog.velcdn.com/images/dan_/post/cd44739a-20b5-480b-b59d-9a6ffc2467bd/image.png" alt=""></p>
<p>즉, if 문의 조건을 돌릴 때, req.params.n과 숫자10을 비교하느라, req.params.n의 타입을 문자열에서 숫자로 자동으로 타입 변환을 한 것</p>
<p>이렇게 자동으로 타입 변환하는 건, 편리하지만, 오류나 문제를 일으킬 수 있기에, parseInt를 활용해 명시적으로 변환해줘야 한다. </p>
<pre><code class="language-jsx">const express = require(&#39;express&#39;)
const app = express()
// express command+클릭하면 파일 확인 가능

app.listen(3000)

app.get
(&#39;/products/:n&#39;, function(req,res){

    let number = parseInt(req.params.n) - 0
    console.log(number)

    res.json({
        num: number
    })
})
</code></pre>
<h2 id="2-유튜브로-reqparams-연습하기">2. 유튜브로 req.params 연습하기</h2>
<h3 id="21--채널-닉네임-불러오기">2.1  채널 닉네임 불러오기</h3>
<pre><code class="language-jsx">const express = require(&#39;express&#39;);
const app = express();
app.listen(3000);

//채널: https://www.youtube.com/@NewJeans_official
//채널: https://www.youtube.com/@essentialme
//채널: https://www.youtube.com/@MBCNEWS11

app.get(&#39;/:nickname&#39;, function(req,res){
    const param = req.params

    console.log(param);

    res.json({
        channel : param.nickname,
    })

});</code></pre>
<ol>
<li><p>express 모듈 불러오기</p>
</li>
<li><p>app 상수에 express 모듈 할당</p>
</li>
<li><p>app.get 메서드를 통해서 url 닉네임 부분 긁어오고 화면에 띄우기</p>
<ol>
<li><p>1번째 매개변수는 Url</p>
</li>
<li><p>2번째는 콜백함수</p>
<ol>
<li><p>콜백함수의 첫 매개변수인 req의 params 메서드 결과를 param 상수에 할당</p>
<ul>
<li><p>console.log한 결과</p>
<pre><code>{ nickname: &#39;@newjeansofficial&#39; }
{ nickname: &#39;favicon.ico&#39; }</code></pre></li>
</ul>
</li>
<li><p>res.json으로 param 객체 상수의 값을 channel이라는 키 값에 할당 후 화면에 띄우기</p>
<p> <img src="https://velog.velcdn.com/images/dan_/post/11dd9f12-1416-45ae-a63e-2e262cb64388/image.png" alt=""></p>
</li>
</ol>
</li>
</ol>
</li>
</ol>
<h3 id="22-채널-query-사용해보기">2.2 채널 query 사용해보기</h3>
<pre><code class="language-jsx">const express = require(&#39;express&#39;);
const app = express();
app.listen(3000);

// 뉴진스 무대: https://www.youtube.com/watch?v=P3HxnZckrEo&amp;t=887s
// /watch 뒤에 ?부터는 query의 영역
// v 키와 P3HxnZckrEo 값, t 키와 887s 값
// 이 두 가지의 값은 req.query 객체로 저장됨

app.get(&#39;/watch&#39;, function(req,res){
    const q = req.query;

    console.log(param);

    res.json({
        video : q.v,
        time: q.t
    })

});</code></pre>
<p>const q = req.query;에 v=P3HxnZckrEo&amp;t=887s 저장됨</p>
<p>res.json() 으로 video : q.v, time: q.t 객체로 전달됨</p>
<h3 id="참고-url-간단-구조">(참고) URL 간단 구조</h3>
<blockquote>
<p><a href="https://www.youtube.com/watch?v=P3HxnZckrEo&amp;t=887s">https://www.youtube.com/watch?v=P3HxnZckrEo&amp;t=887s</a></p>
</blockquote>
<p><a href="https://www.youtube.com/watch?v=P3HxnZckrEo&amp;t=887s">https://www.youtube.com/watch?v=P3HxnZckrEo&amp;t=887s</a>  -  <code>URL</code></p>
<p><a href="https://www.youtube.com/watch?v=P3HxnZckrEo&amp;t=887s">https</a> -  <code>프로토콜</code></p>
<p><a href="https://www.youtube.com/watch?v=P3HxnZckrEo&amp;t=887s">www.youtube.com</a> - <code>도메인</code></p>
<p>/<a href="https://www.youtube.com/watch?v=P3HxnZckrEo&amp;t=887s">watch</a> - <code>Path</code> (/)(파일의 경로)</p>
<p><a href="https://www.youtube.com/watch?v=P3HxnZckrEo&amp;t=887s">?v=P3HxnZckrEo&amp;t=887s</a>  - <code>parameter</code> (?)(key=value 형태로 이루어져있고, &amp; 기호로 구분되어서 여러 개 존재 가능)(req.query로 관리)</p>
<h3 id="참고-query-params">(참고) query, params</h3>
<h3 id="23-구조분해할당">2.3 구조분해할당</h3>
<pre><code class="language-jsx">const array = [1,2,3,4,5] 

const [, num2, num3, , num5] = array
console.log(num2) //2
console.log(num5) //5</code></pre>
<p>자바스크립트 배열 비구조화
배열은 인덱스값을 가지기에, 순서대로 구조분해할당이 됨</p>
<pre><code class="language-jsx">//구조분해할당 X
app.get(&#39;/:nickname&#39;, function(req,res){
    const param = req.params

    res.json({
        channel : param.nickname
    })

});</code></pre>
<pre><code class="language-jsx">//구조분해할당 O
app.get(&#39;/:nickname&#39;, function(req,res){
    const {nickname} = req.params;

    res.json({
        channel: nickname
    })
})</code></pre>
<h3 id="24-유튜브-실습-2---객체-생성-후-테스트">2.4 유튜브 실습 2 - 객체 생성 후 테스트</h3>
<pre><code class="language-jsx">let youtuber1 = {
    channelTitle : &#39;MBCnews&#39;,
    sub : &#39;434만명&#39;,
    videoNum : &#39;24만개&#39;

}

let youtuber2 = {
    channelTitle : &#39;essential&#39;,
    sub : &#39;137만명&#39;,
    videoNum : &#39;391개&#39;

}

let youtuber3 = {
    channelTitle : &#39;뉴진스&#39;,
    sub : &#39;600만명&#39;,
    videoNum : &#39;500개&#39;

}

app.get(&#39;/:nickname&#39;, function(req,res){
    const {nickname} = req.params;

    if (nickname == &#39;@MBCNEWS11&#39;){
        res.json(youtuber1)
    } else if (nickname == &#39;@essentialme&#39;){
        res.json(youtuber2)
    } else if (nickname == &#39;@NewJeans_official&#39;){
        res.json(youtuber3)
    } else {
        res.json(&#39;찾으시는 채널이 없습니다.&#39;)
    }

})</code></pre>
<p><a href="http://localhost:3000/@NewJeans_official">http://localhost:3000/@NewJeans_official</a></p>
<p>검색 하면, 다음의 화면 나옴</p>
<p><img src="https://velog.velcdn.com/images/dan_/post/da96b71a-fada-4fa4-b8ee-ebb153da45f0/image.png" alt=""></p>
<h3 id="26-네이밍-룰">2.6 네이밍 룰</h3>
<ol>
<li>파스칼(PascalCase) - 첫 글자, 두번째 단어 첫 글자 모두 대문자</li>
<li>낙타(camelCase) - 두번째 단어 첫글자 대문자</li>
<li>케밥(kebab-case) - 단어 사이 연결은 하이픈(-)</li>
<li>스네이크(snake_case) - 단어 사이 연결은 언더바(_)</li>
</ol>
<blockquote>
<p>네이밍 관행</p>
</blockquote>
<ol>
<li><code>파일, 폴더</code><ol>
<li>두 개 이상의 단어 합쳐서 쓰면, 첫 번째 단어와 두 번째 단어 사이에 알파벳 소문자, 하이픈 - 사용 </li>
<li>예시) demo-api</li>
</ol>
</li>
<li><code>변수, 함수</code><ol>
<li>두 개 이상의 단어 합쳐서 쓰면, 두번째 단어의 첫 글자를 대문자 사용</li>
<li>예시) channelTitle</li>
</ol>
</li>
</ol>
<h2 id="3-map">3. Map</h2>
<h3 id="31-map이란">3.1 map이란</h3>
<pre><code class="language-jsx">let db = new Map();

db.set(1, &quot;notebook&quot;) //key로 value를 찾을 수 있는 한쌍 지정
db.set(2, &#39;cup&#39;);
db.set(3, &#39;chair&#39;);

console.log(db);

console.log(db.get(1));
console.log(db.get(2));
console.log(db.get(3));</code></pre>
<p>Map 생성자 함수로 db 변수 생성</p>
<p>set 메서드로 Map 안에 key, value 추가</p>
<p>get 메서드로 Map 안의 key, value 가져오기</p>
<h3 id="32-express--map">3.2 express + map</h3>
<pre><code class="language-jsx">const express = require(&#39;express&#39;);
const app = express();
app.listen(3000);

let db = new Map();

db.set(1, &quot;notebook&quot;) 
db.set(2, &#39;cup&#39;);
db.set(3, &#39;chair&#39;);

console.log(db);

console.log(db.get(1));
console.log(db.get(2));
console.log(db.get(3));

app.get(&#39;/:id&#39;, function(req,res){
    let {id} = req.params
    id = parseInt(id)
    if (db.get(id) == undefined){
        res.json({
            message: &#39;없는 상품입니다.&#39;
        })
    } else {
        res.json({
            id : id,
            productName: db.get(id)
        })
    }
})</code></pre>
<ol>
<li>구조분해할당으로, id 값을 id 상수에 할당</li>
<li>parseInt 함수로 문자열 → 숫자로 변경</li>
<li>db에서 id키 값을 이용해서 value값 찾고, 없으면 ‘없는 상품입니다’ 띄우기</li>
<li>있으면, res.json으로 화면에 id 값과, id 키에 해당하는 value 값 객체로 전달해서 띄우기</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[데브코스] Node.js express]]></title>
            <link>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-Node.js-express</link>
            <guid>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A4-Node.js-express</guid>
            <pubDate>Fri, 26 Apr 2024 11:05:00 GMT</pubDate>
            <description><![CDATA[<h2 id="1-http-vs-express">1. HTTP vs express</h2>
<p>http는 내장모듈</p>
<p>express는 외부 모듈</p>
<p>둘 다 웹 서버 모듈이지만 express가 좀 더 간결</p>
<ul>
<li>http 사용하기(내장 모듈이라 npm 불필요)</li>
</ul>
<pre><code class="language-jsx">let http = require(&#39;http&#39;); // 내장 모듈(npm 불필요)

function onRequest(request, response){
    response.writeHead(200, {&#39;Content-type&#39; : &#39;text.html&#39;});
    response.write(&#39;Hello Node.js&#39;);
    response.end();
}

http.createServer(onRequest).listen(8888); 
// http 모듈의 createServer 함수에 할일을 다 한 다음에
// onRequest 콜백 함수를 실행시킴</code></pre>
<ul>
<li>npm으로 express 모듈 설치 후 사용하기</li>
</ul>
<pre><code class="language-jsx">npm install express</code></pre>
<pre><code class="language-jsx">const express = require(&#39;express&#39;)
const app = express()

app.get(&#39;/&#39;, function (req, res) {
  res.send(&#39;Hello World&#39;)
})

app.listen(3000)</code></pre>
<ol>
<li>내장 메서드인 http랑 형태가 다름, 좀 더 간결</li>
<li>app.listen()  이 부분은 어디에 있든지 상관 없음, 순서 상관 없음</li>
<li>express를 command클릭하면 세부 파일로 이동 가능</li>
</ol>
<p><img src="https://velog.velcdn.com/images/dan_/post/623e59eb-06b9-4db1-a1b1-1db8bad59157/image.png" alt=""></p>
<ul>
<li>추가적인 작업해보기(/test, /test/1 경로 추가해보기)</li>
</ul>
<pre><code class="language-jsx">const express = require(&#39;express&#39;)
const app = express()

app.get(&#39;/&#39;, function (req, res) {
  res.send(&#39;hello world&#39;)
})

// API: GET + &quot;http://localhost:3000/test&quot;
// &quot;test success&quot;
app.get(&#39;/test&#39;, function (req, res){
    res.send(&quot;test success&quot;)
})

// API: GET + &quot;http://localhost:3000/test/1&quot;
// &quot;one!&quot;
app.get(&#39;/test/1&#39;, function (req, res){
    res.send(&quot;one!&quot;)
})

app.listen(3000)</code></pre>
<h2 id="2-객체로-작업하자-json">2. 객체로 작업하자, JSON</h2>
<pre><code class="language-jsx">app.get(&#39;/products/1&#39;, function(req,res){
    res.send(&#39;Node.js를 배워보자(책)&#39;);
    res.send(2000);
})</code></pre>
<p>이렇게 보내게 되면, 첫 번째 코드인 Node.js를 배워보자(책)만 화면에 출력됨.</p>
<p>뒷 줄부터는 출력이 안된다!</p>
<p>따라서, 데이터를 텍스트가 아니라 객체 형태로 보내야 한다</p>
<ul>
<li>객체란?</li>
</ul>
<p>우리 세상은 다 객체로 이루어져있다</p>
<p>교과서적으로는 0개 이상의 프로퍼티 혹은 메서드로 구성된 집합이라고 볼 수 있다.</p>
<p>좀 다르게 해석하면 주어 자리에 있을 때 문장이 만들어지면 그건 다 객체다.</p>
<h3 id="21-json">2.1 JSON</h3>
<p>JavaScript Object Notation</p>
<p>즉, 자바스크립트 객체 표기법이다.</p>
<p>임의의 객체 생성해보자</p>
<pre><code class="language-jsx">const book = {
    title : &#39;Node.js를 공부해보자&#39;,
    price : 20000,
    description: &#39;이 책 좋음&#39;
}</code></pre>
<p>send 메서드는 객체든, html이든 모든 종류 다 보내주는 메서드이고</p>
<pre><code class="language-tsx">    /**
     * Send a response.
     *
     * Examples:
     *
     *     res.send(new Buffer(&#39;wahoo&#39;));
     *     res.send({ some: &#39;json&#39; });
     *     res.send(&#39;&lt;p&gt;some html&lt;/p&gt;&#39;);
     *     res.status(404).send(&#39;Sorry, cant find that&#39;);
     */</code></pre>
<p>좀 구체적이고 명확하게 객체를 보내고 싶으면, json 메서드 사용하기, 객체 형태로 넣기</p>
<pre><code class="language-jsx">const express = require(&#39;express&#39;)
const app = express()

app.listen(3000)

app.get(&#39;/products/1&#39;, function(req,res){
    res.json({
        title : &#39;Node.js를 공부해보자&#39;,
    price : 20000,
    description: &#39;이 책 좋음 왜? 김송아가 지음&#39;
})</code></pre>
<p>객체를 따로 빼서 작업하는 게 좀 더 객체 지향적인 모습</p>
<pre><code class="language-jsx">const express = require(&#39;express&#39;)
const app = express()

app.listen(3000)

let nodejsbook = {
    title : &#39;Node.js를 공부해보자&#39;,
    price : 20000,
    description: &#39;이 책 좋음 왜? 김송아가 지음&#39;
}

app.get(&#39;/products/1&#39;, function(req,res){
    res.json(nodejsbook);
})</code></pre>
<h2 id="3-params">3. params</h2>
<p>다음의 상황 가정해보자.</p>
<p>product/1 url에서는 1을 프린팅,</p>
<p>product/2 url에서는 2를 프린팅,</p>
<p>product/3 url에서는 3을 프린팅</p>
<p>3개 정도야 하드코딩이 가능하더라도</p>
<pre><code class="language-jsx">app.get(&#39;/products/1&#39;, function(req,res){
    res.send({
        num : 1
    })
})

app.get(&#39;/products/2&#39;, function(req,res){
    res.send({
        num : 2
    })
})

app.get(&#39;/products/3&#39;, function(req,res){
    res.send({
        num : 3
    })
})</code></pre>
<p>1000개씩 작업하려면, 불가능함</p>
<p>효율적으로 작업하기 위해서 params이 존재</p>
<pre><code class="language-jsx">const express = require(&#39;express&#39;)
const app = express()

app.listen(3000)

app.get(&#39;/products/:n&#39;, function(req,res){
    // : -&gt; 어? 나한테 url로 매개변수 전달해주나보다?
    // products/_ 빈칸에 오는 값을 n이라는 변수에 담아줘!
    // req.params
    res.json({
        num: req.params.n
    })
})</code></pre>
<p>app.get() 이 블록 자체가 라우트를 정의하는데 사용되는 매서드.
이 메서드는 HTTP GET 요청을 처리하는 새로운 라우트를 어플리케이션에 추가함.</p>
<p>첫 매개변수는 경로(path, 클라이언트가 서버에 보내는 요청의 URL 일부)이고, 두 번쨰 매개변수는 해당 경로에 대한 요청을 처리하는 콜백 함수이다.</p>
<p>콜백 함수에는 (req, res)라는 두 매개변수 존재</p>
<blockquote>
<p>req</p>
</blockquote>
<p>req는 HTTP 요청에 대한 전체 정보 포함하고 있음</p>
<ol>
<li><p>req.url: 요청된 URL전체 경로와 쿼리 문자열을 나타내는 문자열</p>
</li>
<li><p>req.params: 라우트의 경로 매개변수에 대한 정보를 담고 있는 객체</p>
</li>
<li><p>req.query: 쿼리 매개변수에 대한 정보 담고 있는 객체 </p>
</li>
<li><p>req.method: HTTP 메서드를 나타내는 문자열 </p>
<p>등등 이외 여러 정보 포함...</p>
</li>
</ol>
<p>/localhost/:n라는 경로가 있으면(:n은 URL에서 동적으로 변경되는 부분)
req.params는 객체고, req.params.n은 해당 매개변수 값에 접근하는 방식
req.params.n은 :n에 해당되는 값</p>
<p>예를 들어, /products/123과 같은 URL에서 123은 :n에 해당함.</p>
<blockquote>
<p>res</p>
</blockquote>
<p>req에서 얻은 이 값을 응답으로 클라이언트에게 JSON 형식으로 전송한다.
요청이 /products/123이라면, 응답으로는 { num: &#39;123&#39; }이 반환된다.</p>
<p>참고로, res도 객체라서, 여러 메서드가 존재함.</p>
<ol>
<li><p>res.send: 문자열, 객체, 배열 등</p>
</li>
<li><p>res.json: JSON 응답</p>
</li>
<li><p>res.render: 뷰 탬플릿을 렌더링해서 HTML 페이지 생성</p>
<p>등등</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[데브코스][3-2] Node.js]]></title>
            <link>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A43-2-Node.js</link>
            <guid>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A43-2-Node.js</guid>
            <pubDate>Thu, 25 Apr 2024 14:48:39 GMT</pubDate>
            <description><![CDATA[<h2 id="1-http-메서드">1. HTTP 메서드</h2>
<p>HTTP에 담아보내는 나의 <code>목적</code> = HTTP <code>method</code></p>
<p>HTTP = 규약 </p>
<p>따라서, 정해진 용어가 존재하고 적절한 method를 사용해야 함</p>
<blockquote>
<p>method</p>
</blockquote>
<ul>
<li>생성: POST</li>
<li>조회: GET</li>
<li>수정<ul>
<li>PUT(덮어쓰기)</li>
<li>PATCH(부분 수정, 바뀐 값만 수정), 마인 페이지에서 이름만 바꾸면 부분 수정</li>
</ul>
</li>
<li>삭제: DELETE</li>
<li>그외) HEAD, OPTIONS, CONNECT, TRACE</li>
</ul>
<h2 id="2-nodejs">2. Node.js</h2>
<p>백엔드에만 쓰이는 게 아니다!(페이팔, 이베이 등은 node.js를 써서 유명하긴 함)</p>
<p>node.js는 여러 자바스크립트를 위해 운동장 깔아주는 역할, 즉, 자바스크립트가 스크립트 언어 이상으로 프로그래밍 언어 역할을 할 수 있도록 지원하는 플랫폼.</p>
<p>Node.js는 넷플릭스, 에어비엔비, 나사, 우버 등에서 사용됨</p>
<h3 id="21-nodejs-특징">2.1 Node.js 특징</h3>
<ol>
<li><code>싱글스레드</code></li>
<li><code>이벤트 기반</code></li>
<li><code>논블록킹 I/O</code></li>
</ol>
<p>세 가지 특징 쉽게 이해하기</p>
<pre><code class="language-jsx">주방장이 한 명인 주방이 있다고 가정해보자.

주방장이 한 명이면, 주문이 자꾸 밀려들어와도 혼자서, 한명이서 어떻게든 요리를 해내야 한다.

→ `싱글 스레드`

요리 2개가 동시에 들어왔다고 가정해보자. 하나는 라면, 하나는 볶음밥이다.

라면의 요리 소요 시간은 총 10분이지만 물 끓이는데 5분이 걸린다.

볶음밥은 바로 5분동안 볶아주면 된다. 

그렇다면, 라면이 끓는동안 아무것도 안하고 라면물만 지켜보는 게 아니라, 그 시간에 볶음밥을 볶아야 옳은 선택이다.

→ `논블록킹 I/O`

요리 주문이 안 들어온다면, 요리를 할 필요가 없다.

→ `이벤트 기반` (**이벤트가 있어야만 일을 한다**(즉, 이벤트가 없다면 일을 안 한다))</code></pre>
<h3 id="22-실습">2.2 실습</h3>
<p>노드 버전 확인 명령어</p>
<pre><code class="language-jsx">node -v</code></pre>
<p>setTimeout(원하는 시간이 지나고 안의 인자로 받은 것 실행해주는 모듈)모듈 사용해보기</p>
<p>순서상으로는 third보다 second가 먼저 출력되어야 하는데, 모듈 덕분에 third가 먼저 출력된 모습</p>
<pre><code class="language-jsx">function first(){
    console.log(&#39;첫 번째&#39;);
}
function second(){
    console.log(&#39;두 번째&#39;);
}
function third(){
    console.log(&#39;세 번쨰&#39;);
}

first();
setTimeout(second, 2000);
third();</code></pre>
<h3 id="23-모듈">2.3 모듈</h3>
<p>모듈은 선배 개발자들이 미리 만들어놓은 코드 덩어리.</p>
<p>어디까지를 모듈이라고 칭할지, 덩어리를 어떻게 정의할지 다 내 마음!</p>
<p>하나의 함수를 각자 마음대로 짜는 것처럼 모듈도 마음대로 짤 수 있음</p>
<p>모듈을 가져다가 쓰는 방법 2가지</p>
<ol>
<li>기본 내장된 것만 쓰기 (마치 앞서 봤던 setTimeout())</li>
<li>외부 모듈 사용하기(npm 명령어 사용)</li>
</ol>
<h3 id="24-라이브러리-vs-프레임워크">2.4 라이브러리 vs 프레임워크</h3>
<p>우리가 도서관에 가면, 엄청 많은 책들이 세부적으로 분류가 되어있고, 우리는 필요한 기준(작가, 내용, 출판사 등)에 따라서 판별해서 책을 고르게 된다.</p>
<p>모든 책을 다 볼 필요도 없고 본인 기준에서 원하는 책을 볼 수 있다는 장점도 있지만, 너무 방대한 선택지 중에서 고르기 어렵거나 힘들고 찾는데 시간이 오래 걸린다는 단점도 있다. </p>
<p>라이브러리는 모듈과 거의 비슷하다고 볼 수 있다.</p>
<p>모듈처럼 이미 만들어진 코드 덩어리이고 우리는 이걸 편의와 목적에 따라서 가져와 쓸 수 있다.</p>
<p>프레임워크는 틀 안에서 일하는 것이다.</p>
<p>내가 만들고 싶은 서비스를 구현하는데 필요한 모든 일을 틀 안에서 이미 존재하는 것이다.</p>
<p>프레임워크는 서비스 완성을 위해 필요한 라이브러리(모듈)을 다 틀 안에 넣어뒀다.</p>
<h3 id="25-npm-figlet-사용해보기">2.5 npm, figlet 사용해보기</h3>
<p>이미 내장된 모듈은 그냥 사용하면 되지만, 외부 모듈을 사용하려면 특별한 명령어가 필요하다. </p>
<p>바로, <code>npm</code></p>
<blockquote>
<p>NPM</p>
</blockquote>
<p>외부 모듈을 내 프로젝트에 설치할 수 있도록 해주는 역할 담당하는 NPM</p>
<ul>
<li>이 명령어로 어떻게 쉽게 설치가 가능하지?</li>
</ul>
<p>우리 입장에서는 간단하지만, npm 입장에서는z 파일 다운받아서 우리 대신에 풀어서 쓸 수 있도록 하는 것</p>
<p>npm이 저장소에 외부 모듈을 설치를 우리가 원하는 프로젝트에 자동으로 해줌</p>
<ul>
<li>설치</li>
</ul>
<pre><code class="language-jsx">npm install 모듈</code></pre>
<ul>
<li>제거</li>
</ul>
<pre><code class="language-jsx">npm uninstall 모듈</code></pre>
<ul>
<li>모듈 설치</li>
</ul>
<pre><code class="language-jsx">npm i 모듈명 -g </code></pre>
<p>내 컴퓨터에 있는 모든 프로젝트에 해당 모듈 설치하는 것 &lt;추천하진 않음&gt;</p>
<p>유명한 모듈 중 하나인 figlet 사용해보는 실습을 해보자.</p>
<p>npm 사이트에서 찾아서 코드 vscode에서 돌려보자.</p>
<p><a href="https://www.npmjs.com/package/figlet">npm: figlet</a></p>
<pre><code class="language-jsx">var figlet = require(&quot;figlet&quot;);

figlet(&quot;Hello World!!&quot;, function (err, data) {
  if (err) {
    console.log(&quot;Something went wrong...&quot;);
    console.dir(err);
    return;
  }
  console.log(data);
});</code></pre>
<p>결과물</p>
<pre><code>  _   _      _ _        __        __         _     _ _ _ 
 | | | | ___| | | ___   \ \      / /__  _ __| | __| | | |
 | |_| |/ _ \ | |/ _ \   \ \ /\ / / _ \| &#39;__| |/ _` | | |
 |  _  |  __/ | | (_) |   \ V  V / (_) | |  | | (_| |_|_|
 |_| |_|\___|_|_|\___/     \_/\_/ \___/|_|  |_|\__,_(_|_)</code></pre><blockquote>
<p>작동 방식</p>
</blockquote>
<p>figlet(매개변수1,매개변수2)</p>
<p>매개변수1은 프린팅하고 싶은 문자</p>
<p>매개변수2는 콜백함수</p>
<p>문자열을 받아서, 아스키 아트를 생성한 후 두번째 매개변수 function 콜백 함수를 실행하는 방식</p>
<ul>
<li>왜 익명의 함수를 전달할까?<ul>
<li>이 함수를 쓸 일이 다른 곳은 없어서, 일회용으로 사용하려고</li>
</ul>
</li>
<li>왜 매개변수로 함수를 받을까?<ul>
<li>figlet을 만든 사람이 매개변수로 함수를 받기로 해서…</li>
</ul>
</li>
</ul>
<blockquote>
<p>콜백 함수 쪼개보기</p>
</blockquote>
<pre><code class="language-jsx">let http = require(&#39;http&#39;); // 내장 모듈(npm 불필요)

function onRequest(request, response){
    response.writeHead(200, {&#39;Content-type&#39; : &#39;text.html&#39;});
    response.write(&#39;Hello Node.js&#39;);
    response.end();
}

http.createServer(onRequest).listen(8888); 
// http 모듈의 createServer 함수에 할일을 다 한 다음에
// onRequest 콜백 함수를 실행시킴</code></pre>
<p>이전에 작업했던 HTTP 서버 파일</p>
<p>http.createServer(onRequest).listen(8888); </p>
<p>이 부분에서 HTTP 모듈의 createServer 함수에 할일을 다 한 다음에</p>
<p>onRequest 콜백 함수를 실행시키는 것 확인 가능</p>
<aside>
⚠️ 참고) var, let, const

</aside>

<pre><code>var - 함수레벨 스코프, 전역 변수

let - 블록 레벨 스코프

const - 블록 레벨 스코프, 상수, 초기 할당 이후 변경 불가능</code></pre><aside>
⚠️ 참고) 탬플릿 문자열

</aside>

<pre><code>템플릿 문자열 도입 전</code></pre><pre><code class="language-jsx">console.log(num1 + &#39;X&#39; + num2 +  &quot;=&quot; + num3);</code></pre>
<pre><code>템플릿 문자열 도입 후</code></pre><pre><code class="language-jsx">console.log(`${num1} X ${num2} = ${num3}`);</code></pre>
<p>백틱과 ${}를 이용해서 간단하게 문자열 생성 가능, 특히나 변수 삽입이 매우 쉬워짐</p>
<h3 id="26-모듈-지우기">2.6 모듈 지우기</h3>
<pre><code class="language-jsx">npm uninstall figlet

// 결과
// removed 1 package, and audited 12 packages in 535ms</code></pre>
<p>npm uninstall 명령어 사용하면 됨</p>
<p>모듈 객체는 package.json에서 확인 가능</p>
<p><img src="https://velog.velcdn.com/images/dan_/post/d06a482e-41be-4b47-ac94-5bb7933f9139/image.png" alt=""></p>
<p>figlet 모듈 설치 전에는 객체 안에 아무것도 없었는데 반해서</p>
<p><img src="https://velog.velcdn.com/images/dan_/post/cfd155fc-c2a0-4395-be1b-e9d0e67b1a16/image.png" alt=""></p>
<p>설치 후에는 figlet 정보 확인 가능</p>
<p>dependencies는 사용할 것들에 대한 키</p>
<blockquote>
<p>package.json vs package-lock.json</p>
</blockquote>
<p>후자는 좀 더 구체적인 정보를 담은 파일</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[데브코스][3-1] 백엔드 기초, API란? ]]></title>
            <link>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A43-1-%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B8%B0%EC%B4%88-API%EB%9E%80</link>
            <guid>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A43-1-%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B8%B0%EC%B4%88-API%EB%9E%80</guid>
            <pubDate>Wed, 24 Apr 2024 13:49:12 GMT</pubDate>
            <description><![CDATA[<h2 id="1-백엔드-기초">1. 백엔드 기초</h2>
<p><img src="https://velog.velcdn.com/images/dan_/post/dadc7486-c665-4aeb-99c6-29edaa3b9990/image.png" alt=""></p>
<p>웹 서비스는 정적 페이지에 대해 대응(변동이 없음)</p>
<p>웹 어플리케이션부터 동적 페이지 작업 가능(변동이 존재, 데이터 처리와 연산 등이 발생, 매번 페이지 직접 바꾸는 게 아니라 자동으로 변하도록)</p>
<h3 id="11-api">1.1 API</h3>
<p>API는 application programming interface의 약자</p>
<blockquote>
<p>인터페이스란?</p>
</blockquote>
<p>중간에서 양쪽에 있는 친구들을 중재/매개체가 되어주는 역할</p>
<p>GUI graphic user interface</p>
<p>컴퓨터(프로그램)한테 명령 내릴 때, 그래픽을 사용해서 명령을 내리는 방식</p>
<p>CLI command line interface</p>
<p>명령어 문장(줄)로 프로그램에게 명령을 내리는 방식</p>
<blockquote>
<p>API란?</p>
</blockquote>
<p>서울 교통공사의 데이터베이스를 사용하고 싶다고 가정해보자.</p>
<p>데이터베이스를 이용하고 싶은 개인에게 수정 권환 줘도 되나?</p>
<p>어떻게 개인은 어플 만들까?</p>
<p>사용자가 직접 서울 교통 공사 데이터베이스에 접근하는 게 아니라 한겹 씌워진 API라고 하는 곳에 요청</p>
<p>우리는 API라는 인터페이스를 통해서 여러 요청을 하고 응답도 받음</p>
<p>백엔드는 API, 데이터베이스 모두 만드는 직업</p>
<h3 id="12-rest-api">1.2 REST API</h3>
<blockquote>
<p>API 형식</p>
</blockquote>
<p>API는 데이터 아무렇게나 주면 되는 거임?</p>
<p>웹은 인터넷망 속의 가상 공간.</p>
<p>웹 백엔드이든 앱 백엔드이든</p>
<p>원격으로 통신하게 된다면, 우리는 무조건 인터넷망의 약속 규약을 지켜야 한다.</p>
<p>웹은 특히나 인터넷을 돌아다니기 위한 규약을 지켜야 한다.</p>
<blockquote>
<p>HTTP</p>
</blockquote>
<p>HTTP는 마치 편지의 형식같은 느낌</p>
<p>웹 개발자는 HTTP를 지켜야 함 (Hypertext Transfer Protocol)</p>
<p>과거에는 프로토콜 형식을 따르지 않았다.</p>
<p>그러다가 HTTP 창시자가 효율 극대화를 위해 형식 따르는 것 권유 </p>
<p>REST API는 HTTP 규약을 잘 따른 API</p>
<p>REST API vs RESTful API</p>
<p>전자는 HTTP 규약 잘 따른 경우</p>
<p>후자는 너무너무 레스트스러운, 즉 HTTP 규약을 매우매우 잘 따른 경우</p>
<h3 id="13-http에-담아-보내야하는-것들">1.3 HTTP에 담아 보내야하는 것들</h3>
<p>인터넷으로 연결된 클라이언트와 서버는 웹 프로토콜인 HTTP를 사용해서 데이터를 주고 받는다.</p>
<p>인터넷 상에서 공유/전달하고 싶은 모든 것들은 다 HTTP에 넣어서 보내야 한다.</p>
<blockquote>
<p>목적의 중요성</p>
</blockquote>
<p>바디에는 실제로 전달해줄 데이터 값 전달 가능</p>
<p>바디에는 데이터 전달 뿐만 아니라</p>
<p>요청 시에 이 데이터 좀 줄래?라고 말하면서</p>
<p>데이터 달라고 하는 목적도 말할 수 있음</p>
<p>전체 상품보고 싶어 = 전체 상품 리스트를 주겠지 → “조회”의 목적</p>
<p>RESTful스러울려면 목적 밝히는 것이 중요하다.</p>
<blockquote>
<p>url</p>
</blockquote>
<p>인터넷 상에서 웹 페이지가 어디있는지 “위치”를 알려주는 것 + 데이터 연산 해달라고 서버에 요청 보내는 방법!!!</p>
<p>즉 웹 페이지 주소라고 생각하면 됨</p>
<p><a href="http://localhost:8888">http://localhost:8888</a> </p>
<p>localhost: 내 컴터 주소</p>
<p>세부요청을 할 때 같은 주소를 보내면 안된다.</p>
<p><a href="http://localhost">localhost</a> 뒤의 내용이 달라야 한다. 즉, 내 컴퓨터 주소 뒤로 뭔가를 추가해야 한다.</p>
<p>(예시)</p>
<p><a href="https://www.lotteon.com/p/display/main/lottemart">https://www.lotteon.com/p/display/main/lottemart</a> </p>
<p>롯데마트라는 전체 몰에서 롯데마트라는 세부 사항 url</p>
<h3 id="14-url--method-연습-1">1.4 URL + method 연습 1</h3>
<p>RESPapi 스러운 URL 규칙</p>
<ol>
<li>소문자만 사용</li>
<li>언더바(_) 사용 안함, 대신 하이픈(-) 사용</li>
<li>마지막에 / 포함 안 함</li>
<li>행위를 포함하지 않는다.(⚠️) → 목적을 포함하지 않음. 메소드로 빼낼 수 있는 건 포함하지 않고 따로 method로</li>
<li>파일 확장자 포함하지 않는다</li>
<li>복수형을 쓴다.</li>
</ol>
<blockquote>
<p>수정 전</p>
</blockquote>
<ul>
<li>상품 등록 <a href="http://localhost:8888/get">http://localhost:8888/get</a> product</li>
<li>전체 상품 조회 <a href="http://localhost:8888/select_all_products">http://localhost:8888/select_all_products</a></li>
<li>전체 상품 삭제 <a href="http://localhost:8888/DeleteAllProducts">http://localhost:8888/DeleteAllProducts</a></li>
</ul>
<blockquote>
<p>RESTapi스럽게 수정</p>
</blockquote>
<ul>
<li>상품 등록 <a href="http://localhost:8888">http://localhost:8888</a> ⇒ “POST” /product</li>
<li>전체 상품 조회 <a href="http://localhost:8888">http://localhost:8888</a> ⇒ “GET” /products<ul>
<li>all-products X, 어차피 products 복수형 안에 all의 의미 포함</li>
</ul>
</li>
<li>전체 상품 삭제 <a href="http://localhost:8888">http://localhost:8888</a> ⇒ “DELETE” /products</li>
</ul>
<h3 id="15-api-사용법">1.5 API 사용법</h3>
<p>세부적으로 API 사용 방법에 대해서 쇼핑몰 예시를 통해서 생각해보자.</p>
<ol>
<li><p>전체 상품 조회</p>
<p><img src="https://velog.velcdn.com/images/dan_/post/af2b18f0-0bf9-4ff8-b7f2-7d5644cfc235/image.png" alt=""></p>
</li>
</ol>
<ol start="2">
<li>세부 상품 조회
 <img src="https://velog.velcdn.com/images/dan_/post/11b9b3a0-43a7-42f1-8c79-b8ae2c4e36e2/image.png" alt=""></li>
</ol>
<ol start="3">
<li><p>전체 상품 관리 </p>
<p> <img src="https://velog.velcdn.com/images/dan_/post/9d984cae-6e95-4dce-a289-445d9d88ff54/image.png" alt=""></p>
</li>
</ol>
<ol start="4">
<li><p>세부 상품 관리</p>
<p> <img src="https://velog.velcdn.com/images/dan_/post/44a78ba5-774d-40ad-8a75-df2226e2999d/image.png" alt=""></p>
</li>
</ol>
<blockquote>
<p>API 설계</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/dan_/post/02ace09b-82ad-43bb-96fd-3e7225844b40/image.png" alt=""></p>
<p>상품 전체 조회</p>
<ul>
<li>메서드는 GET</li>
<li>path는 /products</li>
</ul>
<p>상품 개별 조회</p>
<ul>
<li>메서드는 GET</li>
<li>path는 /products/{id}<ol>
<li>전체 상품 중에서 개별 상품이라서 products 복수형</li>
<li>전체 상품에서 개별 상품을 조회하니까, 전체상품 조회하는 부분까지는 통일하고 세부경로만 변수로 설정하는 것이 효율적임</li>
</ol>
</li>
</ul>
<p>상품 수정</p>
<ul>
<li>메서드는 PUT</li>
<li>path는 /products/{id}</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[데브코스][2-5] 데이터베이스]]></title>
            <link>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A42-5-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4</link>
            <guid>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A42-5-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4</guid>
            <pubDate>Mon, 22 Apr 2024 14:34:21 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[[데브코스][2-4] 백엔드 기초 + 예시로 서버 페이지 구축]]></title>
            <link>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A42-4-%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B8%B0%EC%B4%88-%EC%98%88%EC%8B%9C%EB%A1%9C-%EC%84%9C%EB%B2%84-%ED%8E%98%EC%9D%B4%EC%A7%80-%EA%B5%AC%EC%B6%95</link>
            <guid>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A42-4-%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B8%B0%EC%B4%88-%EC%98%88%EC%8B%9C%EB%A1%9C-%EC%84%9C%EB%B2%84-%ED%8E%98%EC%9D%B4%EC%A7%80-%EA%B5%AC%EC%B6%95</guid>
            <pubDate>Fri, 19 Apr 2024 14:12:46 GMT</pubDate>
            <description><![CDATA[<h2 id="1-쇼핑몰-프로젝트-제작">1. 쇼핑몰 프로젝트 제작</h2>
<p><img src="https://velog.velcdn.com/images/dan_/post/eb183e64-1c67-4c12-ab1a-ba765a0b136d/image.png" alt=""></p>
<p>그림 상단 order list 클릭 시 order list.html로 이동</p>
<p><img src="https://velog.velcdn.com/images/dan_/post/26a81293-5554-4f2e-8906-612083bdcd87/image.png" alt=""></p>
<p>go home 클릭 시 메인 페이지로 이동</p>
<p>메인 하단의 order 클릭 시 order.html로 이동</p>
<p><img src="https://velog.velcdn.com/images/dan_/post/ce6fc24d-84dd-4293-bc6d-b06dce052f66/image.png" alt=""></p>
<h2 id="2-백엔드">2. 백엔드</h2>
<h3 id="21-백엔드의-구조">2.1 백엔드의 구조</h3>
<p>클라이언트 ↔ 웹 서버</p>
<p><img src="https://velog.velcdn.com/images/dan_/post/2048e576-17d3-469b-a54c-a32b575b4d3b/image.png" alt="">
<img src="https://velog.velcdn.com/images/dan_/post/f1ee5036-dce3-4825-bdce-ed64201be484/image.png" alt="">
<img src="https://velog.velcdn.com/images/dan_/post/d27eee8a-548a-4f23-8af0-d02f1362f849/image.png" alt=""></p>
<p>웹 서버는 정적 페이지에 대응</p>
<p>*<code>정적 페이지</code> - 화면의 내용/데이터 등의 변동이 없는 페이지</p>
<p>*<code>동적 페이지</code> - 데이터 처리/연산을 통해 화면의 내용, 데이터가 변하는 페이지</p>
<p><img src="https://velog.velcdn.com/images/dan_/post/b1c03c8c-16b7-453c-9c6f-e122182eddcd/image.png" alt=""></p>
<p>웹 어플리케이션 서버는 <code>동적 페이지 처리 진행</code></p>
<p>필요한 데이터 연산을 위해 데이터베이스와 연결되어 있고, 데이터 조회/수정/삭제에 대한 처리를 요청함</p>
<p>*<code>데이터 베이스</code> -  데이터를 통합해 효율적으로 관리하기 위한 데이터 집합체를 칭함</p>
<h2 id="3-nodejs">3. Node.js</h2>
<p>자바스크립트를 스크립트 언어 이상의 프로그래밍 언어 역할이 가능하도록 지원하는 플랫폼</p>
<p>node js를 이용해 자바스크립트로 백엔드 구현 가능</p>
<p>자바스크립트에게 운동장 제공</p>
<p>node js라는 운동장에서 활동할 수 있게 됨</p>
<pre><code class="language-jsx">//server.js

let http = require(&#39;http&#39;); // http 모듈은 node js가 가진 기존 모듈 중 하나

function onRequest(request, response){
    response.writeHead(200, {&#39;Content-Type&#39; : &#39;text/html&#39;});
    response.write(&#39;Hello.js&#39;); //writebody 요약
    response.end();
}

http.createServer(onRequest).listen(8888);
// localhost:8888
// 우리가 만든 함수로 서버가 일 할건데, 클라이언트가 그걸 받을려면 8888 주파수로 맞춰야 한다

// 클라이언트랑 서버가 대화하려면 주파수가 같아야 함
// 프로토콜 마치 편지 쓸 떄의 형식처럼
// http 프로토콜 템플릿 존재</code></pre>
<p>작성 후 node.js 실행하려면</p>
<p>터미널에서 node 파일명</p>
<p>중단하려면 ctrl + c</p>
<h3 id="31-프로토콜">3.1 프로토콜</h3>
<p><img src="https://velog.velcdn.com/images/dan_/post/875e0de4-a8da-45db-a523-493ced40817c/image.png" alt="">
<img src="https://velog.velcdn.com/images/dan_/post/b529ba73-5a85-4fdc-9d43-7e9f8289948c/image.png" alt=""></p>
<pre><code class="language-jsx">//server.js

function onRequest(request, response){
    response.writeHead(200, {&#39;Content-Type&#39; : &#39;text/html&#39;});
    response.write(&#39;Hello.js&#39;);
    response.end();
}</code></pre>
<p>head에는</p>
<ol>
<li>통신상태가 어떤지?<ol>
<li>200 - 정상이다</li>
</ol>
</li>
<li>응답이 어떤 형태인지?<ol>
<li>text/html이다.</li>
</ol>
</li>
<li>내용은 “Hello.js”이다.</li>
<li>이제 보낼 내용 끝! → response.end();</li>
</ol>
<h3 id="32-모듈화">3.2 모듈화</h3>
<p>우리 서버도 모듈처럼 만들기</p>
<p>server.js 파일 외에 index.js 파일 생성 후 다음의 코드 삽입 후 터미널에서 서버 돌리면 작동됨</p>
<pre><code class="language-jsx">let http = require(&#39;http&#39;); // http 모듈은 node js가 가진 기존 모듈 중 하나

function onRequest(request, response){
    response.writeHead(200, {&#39;Content-Type&#39; : &#39;text/html&#39;});
    response.write(&#39;Hello.js&#39;);
    response.end();
 }

http.createServer(onRequest).listen(8888);
}</code></pre>
<pre><code class="language-jsx">let server = require(&#39;./server&#39;);</code></pre>
<p>index.js를 통해서 server.js가 실행됨
서버 모듈을 단순히 불러왔다는 사실만으로 켜지면 맘대로 껐다가 켰다가 유연성이 떨어짐</p>
<p>따라서, server.js에서 start 함수 생성 후 안에 기존의 함수 삽입</p>
<p>함수를 부를 때 코드가 실행되도록 하면 됨</p>
<pre><code class="language-jsx">let http = require(&#39;http&#39;); // http 모듈은 node js가 가진 기존 모듈 중 하나

function start(){
    function onRequest(request, response){
        response.writeHead(200, {&#39;Content-Type&#39; : &#39;text/html&#39;});
        response.write(&#39;Hello.js&#39;);
        response.end();
    }

    http.createServer(onRequest).listen(8888);
    // localhost:8888
    // 우리가 만든 함수로 서버가 일 할건데, 클라이언트가 그걸 받을려면 8888 주파수로 맞춰야 한다

    // 클라이언트랑 서버가 대화하려면 주파수가 같아야 함
    // 프로토콜 마치 편지 쓸 떄의 형식처럼
    // http 프로토콜 템플릿 존재


}

exports.start = start;
// 바깥에서 start를 사용할 수 있도록 함
// 현재 코드 안의 start 함수를 바깥에서도 사용할 수 있도록 만들어줌
</code></pre>
<pre><code class="language-jsx">let server = require(&#39;./server&#39;);
// index.js를 통해서 server.js가 실행됨
// 서버 모듈을 단순히 불러왔다는 사실만으로 켜지면 맘대로 껐다가 
// 켰다가 유연성이 떨어짐

server.start();</code></pre>
<p>이렇게 만들고 작동하면 오류가 발생</p>
<ul>
<li>server.js 파일에서는 전역 변수, 함수만 존재해서 globalThis 객체의 프로퍼티로 저장되고, 다른 모듈에서도 접근 가능 (이게 최상위 스코프니까, exports 작업 없이 require 해도 다른 파일에서도 globalThis 객체를 통해서 server.js파일의 function에 접근 가능)</li>
<li>하지만, index.js 파일의 경우, function onRequest 함수 위에 start 함수가 상위 함수로 추가됨. onRequest 함수는 start의 지역 함수라서 start 함수 내부에서만 접근 가능하고, 다른 모듈에서 직접 onRequest 함수에 접근 불가함. 다른 모듈에서도 사용하려면 module.exports를 통해서 명시적으로 내보내기를 해야 함.</li>
<li>전역 함수랑 변수만 존재하는 파일이더라도 혼란스럽게 전역함수가 남용되는 걸 막기 위해서 exports 작업을 명시적으로 진행해줘야 함→ 모듈 간 의존성 명확히하고 코드 가독성과 유지 보수성 높임</li>
</ul>
<h3 id="33-urluniform-resource-locator">3.3 url(uniform resource locator)</h3>
<p>url은 인터넷 상에서 웹 페이지가 어디 있는지 위치를 알려주는 주소.</p>
<p>쉽게 말해서 웹 페이지 주소라고 할 수 있음</p>
<p><a href="http://localhost:8888">http://localhost:8888</a></p>
<p>로컬호스트: 내 컴퓨터 주소(각자의 컴퓨터가 스스로 로컬 호스트라고 칭함)</p>
<pre><code class="language-jsx">//server.js

let http = require(&#39;http&#39;); // http 모듈은 node js가 가진 기존 모듈 중 하나

function start(){
    function onRequest(request, response){
            let pathname = url.parse(request.url).pathname;
            console.log(&#39;pathname : &#39; + pathname);

        response.writeHead(200, {&#39;Content-Type&#39; : &#39;text/html&#39;});
        response.write(&#39;Hello.js&#39;);
        response.end();
    };

    http.createServer(onRequest).listen(8888);

    // localhost:8888
    // 우리가 만든 함수로 서버가 일 할건데, 클라이언트가 그걸 받을려면 8888 주파수로 맞춰야 한다

    // 클라이언트랑 서버가 대화하려면 주파수가 같아야 함
    // 프로토콜 마치 편지 쓸 떄의 형식처럼
    // http 프로토콜 템플릿 존재


}

exports.start = start;
// 바깥에서 start를 사용할 수 있도록 함
// 현재 코드 안의 start 함수를 바깥에서도 사용할 수 있도록 만들어줌
</code></pre>
<p>url 모듈은 url을 통해서 입려된 값을 사용할 수 있도록 도와줌</p>
<p>url 모듈 종류 3가지</p>
<ul>
<li>parse() - 문자열 입력 → 객체 생성</li>
<li>format() - 객체 입력 → 문자열 생성</li>
<li>resolve() → 상대 Url을 절대 url로 변경</li>
</ul>
<h3 id="34-router">3.4 router</h3>
<p>어디로 갈지 경로 정해주는 파일</p>
<p>route + er</p>
<pre><code class="language-jsx">//router.js

function route(pathname){
    console.log(&#39;pathname :&#39; + pathname);
}

exports.route = route;</code></pre>
<p>서버에서 pathname 받아옴</p>
<p>pathname은 서버를 먼저 받아야 하지만</p>
<p>console.log 작업하는 건 route의 역할로 위임</p>
<p>console.log 작업하는 함수 생성 후, 모듈화해서 서버에서 부를 수 있도록 작업</p>
<pre><code class="language-jsx">//server.js

let http = require(&#39;http&#39;); // http 모듈은 node js가 가진 기존 모듈 중 하나

function start(route){
    function onRequest(request, response){
            let pathname = url.parse(request.url).pathname;
            route(pathname);
        response.writeHead(200, {&#39;Content-Type&#39; : &#39;text/html&#39;});
        response.write(&#39;Hello.js&#39;);
        response.end() 
    };

    http.createServer(onRequest).listen(8888);

    // localhost:8888
    // 우리가 만든 함수로 서버가 일 할건데, 클라이언트가 그걸 받을려면 8888 주파수로 맞춰야 한다

    // 클라이언트랑 서버가 대화하려면 주파수가 같아야 함
    // 프로토콜 마치 편지 쓸 떄의 형식처럼
    // http 프로토콜 템플릿 존재


}

exports.start = start;
// 바깥에서 start를 사용할 수 있도록 함
// 현재 코드 안의 start 함수를 바깥에서도 사용할 수 있도록 만들어줌
</code></pre>
<p>모듈화된 route 함수를 통해서 console.log 작업을 진행해야 하는데,</p>
<p>server 파일에서 route 데이터 가져올 방법은?</p>
<p>index.js 파일에서 서버 파일로부터의 데이터를 가지고 기동해주는 역할 진행하니까, start에 매개변수로 route 삽입 후 index가 실행하도록 작업</p>
<pre><code class="language-jsx">//index.js

let server = require(&#39;./server&#39;);
let router = require(&#39;./router&#39;);

server.start(router.route);</code></pre>
<hr>
<h3 id="35-url에-따라-다른-콘솔-찍기">3.5 <strong>Url에 따라 다른 콘솔 찍기</strong></h3>
<p>router가 루트를 분배해서 경로를 지정하면 요청을 처리하는 파일인 requestHandler.js 생성</p>
<pre><code class="language-jsx">//requestHandler.js

function main(){
    console.log(&#39;main&#39;);
}

function login(){
    console.log(&#39;login&#39;);
}

let handle = {};
handle[&#39;/&#39;] = main;
handle[&#39;/login&#39;] = login;

exports.handle = handle;</code></pre>
<pre><code class="language-jsx">//index.js

let server = require(&#39;./server&#39;);
let router = require(&#39;./router&#39;);
let requestHandler = require(&#39;./requestHandler&#39;);

server.start(router.route, requestHandler.handle);</code></pre>
<p>handle 모듈 소환 후, 서버에 route 모듈과 함께 넘겨줌</p>
<pre><code class="language-jsx">//server.js

let http = require(&#39;http&#39;); // http 모듈은 node js가 가진 기존 모듈 중 하나

function start(route, handle){
    function onRequest(request, response){
            let pathname = url.parse(request.url).pathname;
            route(pathname,handle);

        response.writeHead(200, {&#39;Content-Type&#39; : &#39;text/html&#39;});
        response.write(&#39;Hello.js&#39;);
        response.end() 
    };

    http.createServer(onRequest).listen(8888);

    // localhost:8888
    // 우리가 만든 함수로 서버가 일 할건데, 클라이언트가 그걸 받을려면 8888 주파수로 맞춰야 한다

    // 클라이언트랑 서버가 대화하려면 주파수가 같아야 함
    // 프로토콜 마치 편지 쓸 떄의 형식처럼
    // http 프로토콜 템플릿 존재


}

exports.start = start;
// 바깥에서 start를 사용할 수 있도록 함
// 현재 코드 안의 start 함수를 바깥에서도 사용할 수 있도록 만들어줌
</code></pre>
<p>router에서도 </p>
<pre><code class="language-jsx">//router.js

function route(pathname, handle){
    console.log(&#39;pathname :&#39; + pathname);

    handle[pathname]();
}

exports.route = route;</code></pre>
<p><a href="http://localhost:8888/main">http://localhost:8888/main</a></p>
<p><img src="https://velog.velcdn.com/images/dan_/post/43b9ad59-b98d-41bf-b65d-0deb986f4668/image.png" alt=""></p>
<p><a href="http://localhost:8888/login">http://localhost:8888/login</a></p>
<p><img src="https://velog.velcdn.com/images/dan_/post/a22c79a6-6c5d-472d-86ad-261a0064da09/image.png" alt=""></p>
<h3 id="36-url에-따라-프론트엔드에-다른-response-보내기">3.6 URL에 따라 프론트엔드에 다른 response 보내기</h3>
<pre><code class="language-jsx">//requestHandler.js

function main(response){
    console.log(&#39;main&#39;);


  response.writeHead(200, {&#39;Content-Type&#39; : &#39;text/html&#39;});
  response.write(&#39;Main page&#39;);
  response.end();
}

function login(response){
    console.log(&#39;login&#39;);

    response.writeHead(200, {&#39;Content-Type&#39; : &#39;text/html&#39;});
  response.write(&#39;Login page&#39;);
  response.end();
}

let handle = {};
handle[&#39;/&#39;] = main;
handle[&#39;/login&#39;] = login;

exports.handle = handle;</code></pre>
<p>각 requestHandler 함수에 response를 처리할 수 있는 기능 추가</p>
<pre><code class="language-jsx">//router.js

function route(pathname, handle){
    console.log(&#39;pathname :&#39; + pathname);
    if(typeof handle[pathname] === &#39;function&#39;){
        handle[pathname](response)
    }else {
        response.writeHead(404, {&#39;Content-Type&#39; : &#39;text/html&#39;});
      response.write(&#39;찾으시는 페이지가 없습니다. &#39;);
      response.end();
    }
}

exports.route = route;</code></pre>
<ol>
<li>handle이 객체이고 객체의 프로퍼티 값이 함수(일급객체라서)이니까, 그 값에 대해서 함수처럼 사용 가능 (괄호를 사용해 인자도 넣고)</li>
<li>만약, 없는 주소 입력하면, 에러 표시 날 수 있도록 하기<ol>
<li>입력된 pathname이 함수 타입이면 그대로 requestHandler에게 전달해서 추가적인 기능 이어나가도록</li>
<li>하지만, pathname이 함수 타입이 아니라면, requestHandler에 가지 못하고, router 선에서, 오류 페이지 뜨도록 코드 작성</li>
</ol>
</li>
</ol>
<pre><code class="language-jsx">//server.js

let http = require(&#39;http&#39;); // http 모듈은 node js가 가진 기존 모듈 중 하나

function start(route, handle){
    function onRequest(request, response){
            let pathname = url.parse(request.url).pathname;
            route(pathname,handle);

            //response.writeHead(200, {&#39;Content-Type&#39; : &#39;text/html&#39;});
        //response.write(&#39;Hello.js&#39;);
        //response.end(); 
    };

    http.createServer(onRequest).listen(8888);

    // localhost:8888
    // 우리가 만든 함수로 서버가 일 할건데, 클라이언트가 그걸 받을려면 8888 주파수로 맞춰야 한다

    // 클라이언트랑 서버가 대화하려면 주파수가 같아야 함
    // 프로토콜 마치 편지 쓸 떄의 형식처럼
    // http 프로토콜 템플릿 존재


}

exports.start = start;
// 바깥에서 start를 사용할 수 있도록 함
// 현재 코드 안의 start 함수를 바깥에서도 사용할 수 있도록 만들어줌
</code></pre>
<p>start() 함수의 response 이하 부분을 requestHandler가 작업하도록</p>
<pre><code class="language-jsx">//index.js

let server = require(&#39;./server&#39;);
let router = require(&#39;./router&#39;);
let requestHandler = require(&#39;./requestHandler&#39;);

server.start(router.route, requestHandler.handle);</code></pre>
<p><img src="https://velog.velcdn.com/images/dan_/post/c3c70bb0-edd3-4b88-8936-3a50b9a94296/image.png" alt=""></p>
<p>이렇게 없는 경로를 입력하니 Not found 페이지 제작 가능~</p>
<h3 id="번외-내-이름으로-된-경로-페이지-제작하기">(번외) 내 이름으로 된 경로 페이지 제작하기</h3>
<pre><code class="language-jsx">function main(response){
    console.log(&#39;main&#39;);

    response.writeHead(200, {&#39;Content-Type&#39; : &#39;text/html&#39;});
    response.write(&#39;Main page&#39;);
    response.end(); 
}

function login(response){
    console.log(&#39;login&#39;);

    response.writeHead(200, {&#39;Content-Type&#39; : &#39;text/html&#39;});
    response.write(&#39;Login page&#39;);
    response.end();
}

function Daeun(response){
    console.log(&#39;daeun&#39;);

    response.writeHead(200, {&#39;Content-Type&#39; : &#39;text/html&#39;});
    response.write(&#39;Daeun Cho&#39;);
    response.end();
}

let handle = {}; //key:value 객체
handle[&#39;/&#39;] = main;
handle[&#39;/login&#39;] = login;
handle[&#39;/daeun&#39;] = Daeun;

exports.handle = handle;</code></pre>
<p>function에 Daeun이라는 함수 하나 추가 &amp; handle 객체에도 프로퍼티로 추가하기</p>
<p><a href="http://localhost:8888/daeun">http://localhost:8888/daeun</a></p>
<p>페이지 방문 시 다음의 페이지 확인 가능</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[데브코스][2-3] CSS,JS 기초]]></title>
            <link>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A42%EC%A3%BC%EC%B0%A8-4%EC%9D%BC-css-js-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A42%EC%A3%BC%EC%B0%A8-4%EC%9D%BC-css-js-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Thu, 18 Apr 2024 13:47:50 GMT</pubDate>
            <description><![CDATA[<h2 id="1-css">1. CSS</h2>
<h3 id="11-inline-css-속성-찾는-법검색">1.1 Inline CSS 속성 찾는 법(검색)</h3>
<p>HTML에 CSS를 적용하는 방법 3가지</p>
<ol>
<li><p>인라인 - html 태그 안에 같이 작성</p>
<pre><code> &lt;!DOCTYPE html&gt;
 &lt;html lang=&quot;en&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.0&quot;&gt;
     &lt;title&gt;login page&lt;/title&gt;
 &lt;/head&gt;
 &lt;body&gt;
     &lt;h1 style = &quot;color: red; text-align: center;&quot;&gt;Login&lt;/h1&gt;
     &lt;form&gt;
         ID : &lt;input type=&quot;text&quot; style=&quot;font-size:25px&quot;&gt;
         &lt;br&gt;
         PW : &lt;input type=&quot;password&quot; style=&quot;font-size:25px&quot;&gt;
         &lt;br&gt;
         &lt;input type=&quot;button&quot; value=&quot;login&quot; style=&quot;width:100px; height: 30px&quot;&gt;
     &lt;/form&gt;
 &lt;/body&gt;
 &lt;/html&gt;</code></pre></li>
<li><p>내부 스타일 시트 - html 문서 안에 같이 작성</p>
<pre><code class="language-html"> &lt;!DOCTYPE html&gt;
 &lt;html lang=&quot;en&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.0&quot;&gt;
     &lt;title&gt;login page&lt;/title&gt;
     &lt;style&gt;
         h1 {
             color: red; 
             text-align: center;
         }
         .login_inputs {
             font-size: 25px;
         }
         #btn_login {
             font-size: 30px;
             width: 100px;
             height: 50px;
         }
     &lt;/style&gt;
 &lt;/head&gt;
 &lt;body&gt;
     &lt;h1&gt;Login&lt;/h1&gt;
     &lt;form&gt;
         ID : &lt;input class=&quot;login_inputs&quot; type=&quot;text&quot;&gt;
         &lt;br&gt;
         PW : &lt;input class=&quot;login_inputs&quot; type=&quot;password&quot;&gt;
         &lt;br&gt;
         &lt;input id=&quot;btn_login&quot; type=&quot;button&quot; value=&quot;login&quot;&gt;
     &lt;/form&gt;
 &lt;/body&gt;
 &lt;/html&gt;</code></pre>
<ol>
<li>head에 style 시트 생성</li>
<li>안에 태그(h1,h2,…), 클래스(.), 아이디(#)를 이용해서 스타일 생성하고, 원하는 html 태그에 지정해주기</li>
</ol>
</li>
<li><p>외부 스타일 시트 - html 문서 밖에 작성하고 연결</p>
<pre><code class="language-html"> &lt;!DOCTYPE html&gt;
 &lt;html lang=&quot;en&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.0&quot;&gt;
     &lt;title&gt;login page&lt;/title&gt;
     &lt;link rel=&quot;stylesheet&quot; href=&quot;/style.css&quot;&gt;
 &lt;/head&gt;
 &lt;body&gt;
     &lt;h1&gt;Login&lt;/h1&gt;
     &lt;form&gt;
         ID : &lt;input class=&quot;login_inputs&quot; type=&quot;text&quot;&gt;
         &lt;br&gt;
         PW : &lt;input class=&quot;login_inputs&quot; type=&quot;password&quot;&gt;
         &lt;br&gt;
         &lt;input id=&quot;btn_login&quot; type=&quot;button&quot; value=&quot;login&quot;&gt;
     &lt;/form&gt;
 &lt;/body&gt;
 &lt;/html&gt;</code></pre>
<p> 내부 스타일 시트도 한 파일에서 스크립트, 스타일, 인덱스를 다 보기에는 불편해서 따로 css 파일 제작</p>
<p> 파일 제작 후, html 파일에 연결(link)</p>
<p> rel(relation의 약자)</p>
<p> href(hypetrext reference)</p>
</li>
</ol>
<p>참고) 주석처리하기: command + /</p>
<h2 id="2-js">2. JS</h2>
<h3 id="21-js-이해">2.1 JS 이해</h3>
<p>html 요소 선택 후 제어할 수 있는 스크립트 언어</p>
<blockquote>
<p>프로그래밍 언어와 스크립트 언어의 차이?</p>
</blockquote>
<p>스크립트 언어는 독립적인 프로그램을 개발할 수 있는 프로그래밍 언어가 아니라, 프로그램 내부의 구성요소 중 하나로 프로그램을 제어하는 스크립트 역할을 하는 언어이다.</p>
<p>최근 빠르게 발전하는 런타임 환경 덕분에 스크립터 언어만으로도 충분히 프로그래밍 가능하며 역할이 확장되는 중이다.</p>
<p>HTML에 JS 적용하는 방법은 3가지</p>
<ol>
<li><p>인라인: 사용자와의 상호작용이 있을 때만 가능. ex) 사용자가 버튼 클릭</p>
<pre><code class="language-html"> &lt;!DOCTYPE html&gt;
 &lt;html lang=&quot;en&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.0&quot;&gt;
     &lt;title&gt;login page&lt;/title&gt;
     &lt;link rel=&quot;stylesheet&quot; href=&quot;/style.css&quot;&gt;
 &lt;/head&gt;
 &lt;body&gt;
     &lt;h1&gt;Login&lt;/h1&gt;
     &lt;form&gt;
         ID : &lt;input class=&quot;login_inputs&quot; type=&quot;text&quot;&gt;
         &lt;br&gt;
         PW : &lt;input class=&quot;login_inputs&quot; type=&quot;password&quot;&gt;
         &lt;br&gt;
         &lt;input onclick=&quot;alert(&#39;clicked!&#39;)&quot; id=&quot;btn_login&quot; type=&quot;button&quot; value=&quot;login&quot;&gt;
     &lt;/form&gt;
 &lt;/body&gt;
 &lt;/html&gt;</code></pre>
<p> onclick=&quot;alert(&#39;clicked!&#39;)&quot;</p>
</li>
<li><p>내부 스크립트</p>
<pre><code class="language-html"> &lt;!DOCTYPE html&gt;
 &lt;html lang=&quot;en&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.0&quot;&gt;
     &lt;title&gt;login page&lt;/title&gt;
     &lt;link rel=&quot;stylesheet&quot; href=&quot;/style.css&quot;&gt;
 &lt;/head&gt;
 &lt;body&gt;
     &lt;h1&gt;Login&lt;/h1&gt;
     &lt;form&gt;
         ID : &lt;input class=&quot;login_inputs&quot; type=&quot;text&quot;&gt;
         &lt;br&gt;
         PW : &lt;input class=&quot;login_inputs&quot; type=&quot;password&quot;&gt;
         &lt;br&gt;
         &lt;input onclick=&quot;myfunction()&quot; id=&quot;btn_login&quot; type=&quot;button&quot; value=&quot;login&quot;&gt;
     &lt;/form&gt;

     &lt;script&gt;
         function myfunction (){
             alert(&#39;1&#39;);
             alert(&#39;2&#39;);
             alert(&#39;3&#39;);
         }
     &lt;/script&gt;
 &lt;/body&gt;
 &lt;/html&gt;</code></pre>
<p> 스크립트에 function 생성 후 html 태그에 삽입</p>
<pre><code class="language-html"> &lt;!DOCTYPE html&gt;
 &lt;html lang=&quot;en&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.0&quot;&gt;
     &lt;title&gt;login page&lt;/title&gt;
     &lt;link rel=&quot;stylesheet&quot; href=&quot;/style.css&quot;&gt;
 &lt;/head&gt;
 &lt;body&gt;
     &lt;h1&gt;Login&lt;/h1&gt;
     &lt;form&gt;
         ID : &lt;input id=&quot;txt_id&quot; class=&quot;login_inputs&quot; type=&quot;text&quot;&gt;
         &lt;br&gt;
         PW : &lt;input class=&quot;login_inputs&quot; type=&quot;password&quot;&gt;
         &lt;br&gt;
         &lt;input onClick=&quot;popId()&quot; id=&quot;btn_login&quot; type=&quot;button&quot; value=&quot;login&quot;&gt;
     &lt;/form&gt;

     &lt;script&gt;
         // id란에 입력한 값을 팝업창에 띄우기
         function popId(){
             if (!document.getElementById(&quot;txt_id&quot;).value) {
                 // = document.getElementById(&#39;txt_id&#39;).value == &quot;&quot;
                 alert(&#39;fill the blank&#39;);
             } else {
                 alert(document.getElementById(&quot;txt_id&quot;).value);
             }

         }

         // 나만의 함수 만들고, 버튼 클릭하면 호출하기
         function myfunction (){
             alert(&#39;1&#39;);
             alert(&#39;1&#39;);
             alert(&#39;1&#39;);
         }
     &lt;/script&gt;
 &lt;/body&gt;
 &lt;/html&gt;</code></pre>
<p> document.getElementById</p>
<p> .은 ~중에서, ~의(of)의 의미를 지님</p>
<p> 따라서, 이 코드는 document라는 파일에서 id를 이용해서 요소를 가져온다라는 것</p>
</li>
<li><p>외부 스크립트</p>
<pre><code class="language-html"> &lt;!DOCTYPE html&gt;
 &lt;html lang=&quot;en&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.0&quot;&gt;
     &lt;title&gt;login page&lt;/title&gt;
     &lt;link rel=&quot;stylesheet&quot; href=&quot;/style.css&quot;&gt;
     &lt;script src=&quot;/script.js&quot;&gt;&lt;/script&gt;
 &lt;/head&gt;
 &lt;body&gt;
     &lt;h1&gt;Login&lt;/h1&gt;
     &lt;form&gt;
         ID : &lt;input id=&quot;txt_id&quot; class=&quot;login_inputs&quot; type=&quot;text&quot;&gt;
         &lt;br&gt;
         PW : &lt;input class=&quot;login_inputs&quot; type=&quot;password&quot;&gt;
         &lt;br&gt;
         &lt;input onClick=&quot;popId()&quot; id=&quot;btn_login&quot; type=&quot;button&quot; value=&quot;login&quot;&gt;
     &lt;/form&gt;
 &lt;/body&gt;
 &lt;/html&gt;</code></pre>
<p> <img src="https://velog.velcdn.com/images/dan_/post/322d2647-ca13-4a5d-b51b-66ad713eae2c/image.png" alt=""></p>
</li>
</ol>
<pre><code>script에 연결</code></pre><p>   <img src="https://velog.velcdn.com/images/dan_/post/d99729d0-62dc-4aa1-a799-47f371af1ed2/image.png" alt=""></p>
<h3 id="변수">변수</h3>
<p>변수는 상자</p>
<p>데이터를 담아놓는 상자</p>
<p>변수 생성하는 법</p>
<p>let 상자 이름 = 상자에 담을 데이터; (숫자, 문자, element, 등등)</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&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.0&quot;&gt;
    &lt;title&gt;login page&lt;/title&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;/style.css&quot;&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;Login&lt;/h1&gt;
    &lt;form&gt;
        ID : &lt;input id=&quot;txt_id&quot; class=&quot;login_inputs&quot; type=&quot;text&quot;&gt;
        &lt;br&gt;
        PW : &lt;input class=&quot;login_inputs&quot; type=&quot;password&quot;&gt;
        &lt;br&gt;
        &lt;input onClick=&quot;popId()&quot; id=&quot;btn_login&quot; type=&quot;button&quot; value=&quot;login&quot;&gt;
    &lt;/form&gt;

    &lt;script&gt;
        // id란에 입력한 값을 팝업창에 띄우기
        function popId(){
            let userId = ocument.getElementById(&quot;txt_id&quot;).value;
            if (!userId) {
                // = document.getElementById(&#39;txt_id&#39;).value == &quot;&quot;
                alert(&#39;fill the blank&#39;);
            } else {
                alert(userId);
            }

        }

        // 나만의 함수 만들고, 버튼 클릭하면 호출하기
        function myfunction (){
            alert(&#39;1&#39;);
            alert(&#39;1&#39;);
            alert(&#39;1&#39;);
        }
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>userId라는 변수 생성</p>
<p>if문 조건, else alert 부분에 변수 넣어서 간단하고 가독성 좋게 코드 변경</p>
<pre><code class="language-html">var a; // var는 사용 안함, 예전엔 없었음 이건 이제 let, const로 분리됨
let b;// 변수 선언 후 재할당 가능
const c; // 상자 안에 값이 들어가면 재할당 불가, 엄밀히 말하면, 변수 선언 시 undefined로 초기화된 상태, 이후에 런타임 때 변수값이 재할당되는 거긴 함. 근데 그 이후에 재할당 불가능</code></pre>
<p>텍스트랑 함께 변수 출력하는 법 <code>+</code> 사용 or 백틱</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[데브코스][2-2] 웹 이해, HTML]]></title>
            <link>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A42-2-%EC%9B%B9-%EC%9D%B4%ED%95%B4-HTML</link>
            <guid>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A42-2-%EC%9B%B9-%EC%9D%B4%ED%95%B4-HTML</guid>
            <pubDate>Wed, 17 Apr 2024 08:28:23 GMT</pubDate>
            <description><![CDATA[<h2 id="1-웹의-이해">1. 웹의 이해</h2>
<h3 id="11-웹의-이해">1.1 웹의 이해</h3>
<ul>
<li>인터넷<ul>
<li><code>전세계의 모든 컴퓨터가 하나의 통신망 안에 연결</code>한다는 의미(international network)</li>
</ul>
</li>
<li>월드 와이드 웹<ul>
<li><code>인터넷에 연결된 컴퓨터를 통해 사람들이 정보를 공유할 수 있는 공간</code></li>
<li>간단하게 www, w3 그리고 월드 와이드 웹을 줄여서 웹(web)이라고 부름</li>
<li>인터넷이 있기에 웹이 존재할 수 있는 것</li>
</ul>
</li>
<li>웹 시작?<ul>
<li>스위스 입자 물리 연구소의 컴퓨터과학자 팀 버너스-리가 신속한 정보교환을 위해 고안하게 됨</li>
</ul>
</li>
<li>웹의 특징, 목적<ul>
<li>하이퍼텍스트(단순 글자 아니고 . 그이상의 기능을 가진 텍스트, 주로 링크와 참조의 역할을 하는 기술) 형식으로 표현, 이를 통해 다양한 정보와 문서를 연결,제공</li>
</ul>
</li>
<li>웹 페이지 vs 웹 사이트<ul>
<li>웹 페이지 -  페이지 하나하나, 책 한 장</li>
<li>웹 사이트 - 페이지 모음, 책 전체</li>
</ul>
</li>
<li>웹 브라우저란?<ul>
<li>브라우즈는 an act of casual looking or reading</li>
<li>웹 브라우저는 어떤 것(웹 페이지 또는 웹 상의 데이터)를 찾거나 읽을 때 사용하는 것</li>
</ul>
</li>
</ul>
<h3 id="12-웹의-구조">1.2 웹의 구조</h3>
<p><img src="https://velog.velcdn.com/images/dan_/post/b0676eba-e139-4a4d-800c-9980c5a0d2cd/image.png" alt=""></p>
<p>식당에서 주문 시 클라이언트(고객)가 서버(웨이터)에게 요청</p>
<p>요청받은 서비스, 음식을 제공</p>
<p><img src="https://velog.velcdn.com/images/dan_/post/db2286c9-d7e2-43e3-a1d3-03991f443ab3/image.png" alt=""></p>
<p><code>클라이언트</code>: 서비스를 이용하는 컴퓨터</p>
<p><code>서버</code>: 서비스를 제공하는 컴퓨터</p>
<ul>
<li>클라이언트와 서버 간의 약속, <code>프로토콜</code></li>
</ul>
<p>편지를 보낼때도, 수신인 발신인 내용 끝맺음 등등 구조가 정해져있음</p>
<p>프로토콜이라고 하는 정해진 소통틀이 존재</p>
<p>반드시 이 약속(프로토콜)을 지켜서 통신해야 함</p>
<p><img src="https://velog.velcdn.com/images/dan_/post/271196b3-b6ba-4325-94a7-15cc53d572e0/image.png" alt=""></p>
<p>인터넷으로 연결된 클라이언트와 서버는 웹 프로토콜인 http를 사용해서 데이터를 주고 받는다.</p>
<p>웹은 인터넷으로 연결된 사용자들이 정보를 공유할 수 있는 공간</p>
<h3 id="13-웹-개발-직무-이해">1.3 웹 개발 직무 이해</h3>
<p><img src="https://velog.velcdn.com/images/dan_/post/d6bd39ec-5847-4929-9b95-903ace8caeb5/image.png" alt=""></p>
<p>인터넷 부분을 만지는 일을 잘 없다</p>
<ul>
<li>프론트엔드</li>
</ul>
<p>웹 서비스 클라이언트 부분을 개발하는 게 프론트엔드</p>
<p>사용자 측면에서 화면(그래픽 사용자 인터페이스)로 사용자와 상호작용을 담당함.</p>
<ul>
<li>백엔드</li>
</ul>
<p>인터넷이 정해준 요청 처리하고, 그에 맞는 대응을 해주는 것</p>
<p>이 서버에서 프론트엔드에서 전달받은 데이터와 요청을 내부 데이터와 연산을 활용하고 처리해서 프론트엔드에게 결과 전달</p>
<p><img src="https://velog.velcdn.com/images/dan_/post/4a322d3a-0ab5-4e6a-aa09-ba6ddf5eb18b/image.png" alt=""></p>
<h2 id="2-프론트엔드">2. 프론트엔드</h2>
<h3 id="21-프론트엔드-3대장">2.1 프론트엔드 3대장</h3>
<p>웹은 무엇으로 이루어져있을까?</p>
<p>그 중에서도 웹 프론트엔드는 어떻게 이루어져있을까?</p>
<p>html, css, js</p>
<p>html - 웹 페이지 구성 요소들의 구조</p>
<p><img src="https://velog.velcdn.com/images/dan_/post/9c685abf-9e39-4280-a187-0938c0b3e6bd/image.png" alt=""></p>
<p>css - 웹 페이지 구성 요소들을 꾸밈</p>
<p>js - 웹 페이지 구성 요소들에게 생명력!</p>
<h2 id="3-ide-vscode-설치">3. IDE, VScode 설치</h2>
<p>공방에 가는 이유?</p>
<p>도자기 만들기 위한 프로세스 모두 진행 가능하니까?</p>
<p>한번에 필요한 프로세스를 진행할 환경을 다 제공해주는 역할이 ide</p>
<p>ide는 integrated development environment </p>
<p>통합 개발 환경</p>
<p>vscode는 통합, 확장성 좋아서 인기</p>
<h2 id="4-html">4. HTML</h2>
<p>hyper text markup lanaguage </p>
<p>웹 페이지들을 연결하는 기능을 가진 텍스트이자 웹페이지의 구조를 명시하는 언어</p>
<p>&lt;&gt;는 태그!</p>
<p>이 태그로 html 구현</p>
<p>이 태그들이 웹 페이지의 구성 요소 하나하나의 역할을 하게 됨</p>
<p>태그와 태그 사이에 글자가 있음</p>
<p>태그 형태</p>
<pre><code class="language-markdown">&lt;태그&gt; 감싸진 글자가 그 태그의 역할을 맡습니다. &lt;/태그&gt;</code></pre>
<p>*태그 한 쌍일수도 있고 한 개 태그로 이루어질 수도 있음</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;!-- 이 문서는 html 입니다라는 뜻 --&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;!-- html 파일에 대한 내용 --&gt;
        &lt;meta charset=&quot;UTF-8&quot;&gt;
        &lt;!-- 캐릭터 타입, 한국어를 포함한 영어, 숫자 등을 담고 있는 utf-8을 사용--&gt;
        &lt;title&gt; 첫 번쨰 HTML &lt;/title&gt;
    &lt;/head&gt;
    &lt;body&gt;
        Hello, html!
    &lt;/body&gt;
&lt;/html&gt;</code></pre>
<h3 id="자주-쓰는-html-태그">자주 쓰는 HTML 태그</h3>
<ol>
<li>h<ol>
<li>heading의 약자</li>
</ol>
</li>
<li>a<ol>
<li>Anchor의 약자</li>
<li>href: hypertext reference</li>
</ol>
</li>
<li>br<ol>
<li>break 약수</li>
</ol>
</li>
</ol>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&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.0&quot;&gt;
    &lt;title&gt;Go&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h2&gt;We Can Go&lt;/h2&gt;
    &lt;a href=&quot;https://programmers.co.kr&quot;&gt;Programmers&lt;/a&gt;
    &lt;br&gt;
    &lt;a href=&quot;first.html&quot;&gt;My First Page&lt;/a&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>a href 태그를 통해, 다른 이미 시중의 페이지로도, 내가 제작한 페이지로도 이동 가능</p>
<h3 id="로그인-화면-만들기">로그인 화면 만들기</h3>
<pre><code class="language-markdown">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&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.0&quot;&gt;
    &lt;title&gt;login page&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;Login&lt;/h1&gt;
    &lt;form&gt;
        ID : &lt;input type=&quot;text&quot;&gt;
        &lt;br&gt;
        PW : &lt;input type=&quot;password&quot;&gt;
        &lt;br&gt;
        &lt;input type=&quot;button&quot; value=&quot;login&quot;&gt;
    &lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>form 태그: 입력을 위한 큰 틀</p>
<p>input 태그: 입력 태그, type에 따라서 다양한 입력 받을 수 있음</p>
<p>text가 디폴트</p>
<p>password는 입력시 글자를 가려줌</p>
<p>button은 버튼, value에 버튼 내용을 추가할 수 있음</p>
<p><img src="https://velog.velcdn.com/images/dan_/post/af7c3589-b7a0-4503-a262-7e60eee8c912/image.png" alt=""></p>
<p>로그인 페이지 결과물</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[데브코스][1-5] 깃허브 심화]]></title>
            <link>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A41-5-%EA%B9%83%ED%97%88%EB%B8%8C-%EC%8B%AC%ED%99%94</link>
            <guid>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A41-5-%EA%B9%83%ED%97%88%EB%B8%8C-%EC%8B%AC%ED%99%94</guid>
            <pubDate>Fri, 12 Apr 2024 14:37:55 GMT</pubDate>
            <description><![CDATA[<h2 id="1-깃-브랜치-이름-규칙-및-테스트">1. 깃 브랜치 이름 규칙 및 테스트</h2>
<p>메인 나뭇가지 이름: main(기존명은 master)</p>
<h3 id="1-기능-개발develop">1. 기능 개발(develop)</h3>
<p><code>develop</code> 브랜치를 따고 밑에 <code>feature</code>로 depth를 더 줘서, feature/login, feature/select-product, feature</p>
<h3 id="2-출시-준비release">2. 출시 준비(release)</h3>
<p><code>release</code>-1.3, release-1.4</p>
<h3 id="3-긴급-수정hotfix">3. 긴급 수정(hotfix)</h3>
<p><code>hotfix</code>-1.2.1</p>
<h3 id="브랜치-명령어">브랜치 명령어</h3>
<ol>
<li>이전 브랜치로 이동하는 명령어</li>
</ol>
<pre><code class="language-markdown">git checkout -</code></pre>
<ol>
<li>브랜치 삭제 명령어</li>
</ol>
<pre><code class="language-markdown">git branch -d 브랜치명</code></pre>
<p><img src="https://velog.velcdn.com/images/dan_/post/2597301f-50c4-43b9-9b31-a6a73a8b5cce/image.png" alt=""></p>
<p>*참고로, 내가 현재 머무는 브랜치에서는 해당 브랜치 삭제 불가→ git checkout 명령어로 이동 후 삭제</p>
<ol>
<li>브랜치 생성 명령어</li>
</ol>
<pre><code class="language-markdown">git branch 브랜치명</code></pre>
<ol>
<li>브랜치 목록 확인 명령어</li>
</ol>
<pre><code class="language-markdown">git branch</code></pre>
<ol>
<li>브랜치 이동 명령어</li>
</ol>
<pre><code class="language-markdown">git checkout 브랜치명</code></pre>
<h2 id="2-브랜치는-__부터-진짜다">2. 브랜치는 “__”부터 진짜다?</h2>
<p>바로, commit</p>
<p>브랜치 복사한다고 바로 병렬처리 되는 게 아님</p>
<p>브랜치를 브랜치답게 사용하는 것은 커밋부터</p>
<h2 id="3-원격-브랜치">3. 원격 브랜치</h2>
<p>원격 브랜치 확인</p>
<pre><code class="language-markdown">git branch -r</code></pre>
<p><img src="https://velog.velcdn.com/images/dan_/post/8ed95816-e366-45f0-a1a3-12be0a033068/image.png" alt=""></p>
<p>깃에서 가지는 브랜치는 3개, 원격 깃허브에서 가지는 브랜치는 origin으로 한 개</p>
<p>깃에 만들어둔 브랜치를 원격 브랜치로 복제</p>
<pre><code class="language-markdown">git push 깃허브저장소별칭 깃브랜치명</code></pre>
<p>반대로, 깃허브에서 브랜치 먼저 생성 → 깃으로 받아오기</p>
<pre><code class="language-markdown">git push 깃브랜치명 깃허브저장소별칭</code></pre>
<p><img src="https://velog.velcdn.com/images/dan_/post/a73de964-3a16-434b-ad68-21a074aa08aa/image.png" alt=""></p>
<h2 id="4-깃-브랜치-전략">4. 깃 브랜치 전략</h2>
<p>팀플 시, 브랜치 생성하고, 어떤 단위로 브랜치 만들건지 등</p>
<h3 id="git-flow">git flow</h3>
<p>어떻게 워크 플로우 짜는가</p>
<p>다양한 전략 가능</p>
<p>크게 2가지로 분류 (fast-foward, 3ways)</p>
<p>메인 브랜치는 배포할 브랜치라서 안 건드리는 게 국룰</p>
<h3 id="1-fast-forward-전략">1. fast-forward 전략</h3>
<p>A 브랜치에서 B브랜치 생성</p>
<p>A 브랜치에서는 추가 구현 없고, B 브랜치에서 추가 구현</p>
<p>이후, B브랜치를 A브랜치에 붙이는 방식</p>
<p><img src="https://velog.velcdn.com/images/dan_/post/e18d92c0-7865-4e3b-991d-a7f918cf9664/image.png" alt=""></p>
<p>보통 잘 쓰진 않음</p>
<p>폴더 하나 가지고 개발하는 게 아니기에, 하나의 브랜치가 가만히 있는 경우 희박</p>
<h3 id="2-3ways">2. 3ways</h3>
<p>A 브랜치에서 B브랜치 생성</p>
<p>A 브랜치, B 브랜치 둘 다 추가 구현 후, 서로 비교 후 바뀐 거 정리해서 합치는 전략</p>
<p><img src="https://velog.velcdn.com/images/dan_/post/e6512a05-4779-48e5-b183-80223c24cd01/image.png" alt=""></p>
<p>대부분, 3ways를 진행할 때는 fast-forward도 진행</p>
<h2 id="5-병합과-충돌">5. 병합과 충돌</h2>
<p>PR(pull request) 전 권고 사항</p>
<p>setting → branches → branch protection rule</p>
<p>*근데, private repo에는 필요없는 듯? 확인 필요</p>
<h3 id="pr">PR</h3>
<p><img src="https://velog.velcdn.com/images/dan_/post/ee23ea5b-a6dc-471d-8e41-6e18edfd94dc/image.png" alt=""></p>
<ol>
<li><p>pull request 버튼 누르고</p>
</li>
<li><p>마크다운 형식으로 description 작성</p>
<p> <code>주요 구현 내용</code>, <code>이슈</code> 등에 대해서 작성(구현내용, 이슈는 필수로 작성하기)</p>
<p> pr에 대한 내용, 신경써서 작성하기, 협업 시에 소통하기 좋은 사람이라는 것</p>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/dan_/post/10dc1ffb-7989-4a4e-987d-4f00dd9124ff/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/dan_/post/172e172e-258a-422e-a48c-3a9a0a38fef9/image.png" alt=""></p>
<ol start="3">
<li>merge pull request 진행</li>
</ol>
<p><img src="https://velog.velcdn.com/images/dan_/post/1d928418-b8f1-49c2-8812-b7a52d7cab47/image.png" alt=""></p>
<ol start="4">
<li><p>delete 진행</p>
<p> 보통 브랜치는 사용이 다 끝나면 머지를 시킴</p>
<p> 머지 다 시키면, 브랜치 없애주는 게 좋음
 <img src="https://velog.velcdn.com/images/dan_/post/859a8ef1-27c3-4e01-9d3d-d5c1daaa8c1c/image.png" alt=""></p>
</li>
</ol>
<p>*머지 푸는 거, 브랜치 삭제한 거 복구하는 거 모두 가능</p>
<p>이후, 브랜치 목록 확인하면 브랜치는 메인뿐
<img src="https://velog.velcdn.com/images/dan_/post/853e249f-17ea-4160-813a-f29aebf37dae/image.png" alt=""></p>
<h3 id="병합이란">병합이란</h3>
<p>브랜치를 생성한다는 건, 협업을 위한 것</p>
<p>그래서 우리는 주로 브랜치 병합(추가 가지 → base 가지로 합침)을 깃허브에서 진행</p>
<pre><code class="language-markdown">1. 메인 branch 보호
2. 추가 브랜치 main 브랜치 방향으로 병합 진행(pull request; pr)
3. 충돌 일어나는지 깃허브가 확인(자동으로)
    *PR 메시지 신경써주기(주요 핵심 기능, 이슈)
4. merge 진행(”merge commit”) 병합 시에도 커밋이 발생
5. branch 삭제</code></pre>
<h2 id="6-merge된-깃허브-→-깃에-동기화하기">6. merge된 깃허브 → 깃에 동기화하기</h2>
<p><img src="https://velog.velcdn.com/images/dan_/post/59865845-31fb-4816-9f0b-354a5069105b/image.png" alt=""></p>
<ol>
<li>깃허브 브랜치 목록 동기화하는 명령어</li>
</ol>
<pre><code class="language-markdown">git fetch -p</code></pre>
<ol start="2">
<li>삭제할 브랜치가 아닌 다른 브랜치로 이동</li>
</ol>
<pre><code class="language-markdown">git branch 브랜치명</code></pre>
<ol start="3">
<li>동기화 해주기</li>
</ol>
<pre><code class="language-markdown">git pull origin main</code></pre>
<pre><code>merge 깃허브에서만 진행한 것

따라서, 로컬에서 동기화 진행해줘야 함</code></pre><ol start="4">
<li>삭제</li>
</ol>
<pre><code class="language-markdown">git branch -d 브랜치명</code></pre>
<h2 id="7-충돌-해결하기">7. 충돌 해결하기</h2>
<p>깃허브로 브랜치 생성하기</p>
<p><img src="https://velog.velcdn.com/images/dan_/post/726e7945-aa84-44f6-bcf7-e8edd495e9bc/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/dan_/post/9fe37a78-e669-424f-8d9a-de6742b20c31/image.png" alt=""></p>
<ol>
<li>브랜치 검색</li>
<li>브랜치 없으니 create branch __ from main 클릭</li>
<li>생성 완료</li>
</ol>
<p>깃허브 내의 브랜치 로컬로 받아오기</p>
<pre><code class="language-markdown">git branch -t &quot;브랜치명&quot;</code></pre>
<h3 id="브랜치-충돌">브랜치 충돌</h3>
<p>A폴더와 B폴더에서 똑같은 폴더를 깃허브에서 받아오고 폴더 내의 test1.txt 파일을 수정했다고 가정</p>
<p>A폴더가 수정본 업로드</p>
<p>이후, B폴더의 수정본도 업로드하면, 충돌 발생
<img src="https://velog.velcdn.com/images/dan_/post/41105d14-1c51-428a-809a-906b47015995/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/dan_/post/bb816918-83ca-422c-8678-7fe473270da9/image.png" alt=""></p>
<p>이렇게 같은 부분에 대해서 수정한 것을 확인할 수 있음</p>
<p><img src="https://velog.velcdn.com/images/dan_/post/ec7f44fa-a21a-42ea-9eaf-c4e4fc5eeb7e/image.png" alt=""></p>
<p>이 경우, 확인 후 원하는 코드 남기고 나머지 없애주기</p>
<p>그 다음 상단의 mark as resolved 클릭 후 commit merge하기</p>
<p><img src="https://velog.velcdn.com/images/dan_/post/b8c558f9-f5b7-4faa-9ac4-12587862f92c/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/dan_/post/1c1ee193-bacd-428d-9c62-4c9a9dc178cb/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[데브코스][1-4] 깃허브로 원격 코드 관리, 깃 브랜치 전략 기반의 협업 워크 플로우 ]]></title>
            <link>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A41-4-%EA%B9%83%ED%97%88%EB%B8%8C%EB%A1%9C-%EC%9B%90%EA%B2%A9-%EC%BD%94%EB%93%9C-%EA%B4%80%EB%A6%AC-%EA%B9%83-%EB%B8%8C%EB%9E%9C%EC%B9%98-%EC%A0%84%EB%9E%B5-%EA%B8%B0%EB%B0%98%EC%9D%98-%ED%98%91%EC%97%85-%EC%9B%8C%ED%81%AC-%ED%94%8C%EB%A1%9C%EC%9A%B0</link>
            <guid>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A41-4-%EA%B9%83%ED%97%88%EB%B8%8C%EB%A1%9C-%EC%9B%90%EA%B2%A9-%EC%BD%94%EB%93%9C-%EA%B4%80%EB%A6%AC-%EA%B9%83-%EB%B8%8C%EB%9E%9C%EC%B9%98-%EC%A0%84%EB%9E%B5-%EA%B8%B0%EB%B0%98%EC%9D%98-%ED%98%91%EC%97%85-%EC%9B%8C%ED%81%AC-%ED%94%8C%EB%A1%9C%EC%9A%B0</guid>
            <pubDate>Thu, 11 Apr 2024 14:34:43 GMT</pubDate>
            <description><![CDATA[<h2 id="1-깃허브-가입">1. 깃허브 가입</h2>
<p>깃허브 내에서 회원가입 진행</p>
<p><a href="https://docs.github.com/ko/get-started/start-your-journey/creating-an-account-on-github">GitHub에서 계정 생성하기 - GitHub Docs</a></p>
<h2 id="2-깃허브-레파지토리-create">2. 깃허브 레파지토리 create</h2>
<h3 id="레파지토리란--필요성">레파지토리란 + 필요성?</h3>
<p>우리는 프로젝트 단위로 백업하게 될 것</p>
<p>폴더를 레파지토리라고 생각하면 됨</p>
<p>레파지토리가 있어야, 로컬 컴퓨터의 프로젝트 백업이 가능</p>
<p><code>create repository</code> 버튼으로 생성하기</p>
<p><code>public</code>은 모두에게 공개</p>
<p><code>private</code>은 나만보기</p>
<p><code>readme</code>생성 여부 선택 가능</p>
<p>리드미 없이 하는 게 더 편해서 선택 안하고 추후 추가하는 걸 추천</p>
<p><code>set up github copilot</code></p>
<p>추후 사용하게 될 것</p>
<p>생성되면 다음과 같은 화면이 나타남</p>
<p><img src="https://velog.velcdn.com/images/dan_/post/cb2755e8-722c-4ee0-8f06-6fee621aa917/image.png" alt=""></p>
<h2 id="3-깃허브에-내-로컬-프로젝트-업로드하기">3. 깃허브에 내 로컬 프로젝트 업로드하기</h2>
<p>로컬에서 연결을 시켜주는 것</p>
<h3 id="git-remote-add-origin-url-연결">git remote add origin url (연결)</h3>
<p><img src="https://velog.velcdn.com/images/dan_/post/6f52e0b0-898f-4965-a1ac-80deca06ae4d/image.png" alt=""></p>
<p>깃허브와 로컬을 연결해주는 명령어</p>
<pre><code class="language-markdown">git remote add 원격저장소(레파지토리)별칭 원격저장소url</code></pre>
<p><img src="https://velog.velcdn.com/images/dan_/post/1104ea45-ee19-4114-acbc-b6f6c29092b9/image.png" alt=""></p>
<ul>
<li>fetch 서버의 코드 가져올 때 쓰는 명령어</li>
<li>push - 로컬 코드를 깃허브에 업로드(백업)할 때 쓰는 명령어</li>
</ul>
<h3 id="git-remove--v-확인">git remove -v (확인)</h3>
<p><code>git remove -v</code>를 통해서 연동 여부를 확인 가능</p>
<h3 id="git-push-origin-main-업로드">git push origin main (업로드)</h3>
<pre><code class="language-markdown">git push 원격저장소(레파지토리)별칭 원격저장소레포지토리명(main)</code></pre>
<p><img src="https://velog.velcdn.com/images/dan_/post/6e8eaa57-6d5b-43e4-be0e-d4108a59e8fa/image.png" alt=""></p>
<p>git push origin master를 하면 에러가 뜨지만</p>
<p>git push origin main을 하면 정상적으로 업로드가 되는 모습 확인 가능</p>
<h3 id="결과-1">결과 1</h3>
<p><img src="https://velog.velcdn.com/images/dan_/post/36b397db-e246-42b7-b489-0b2955e3760a/image.png" alt=""></p>
<p>업로드된 걸 확인할 수 있음</p>
<h3 id="결과2">결과2</h3>
<p><img src="https://velog.velcdn.com/images/dan_/post/26e32630-6c5e-4bdd-9b3e-a268f999720d/image.png" alt=""></p>
<p>화살표가 main, origin/main 둘 다 가르키게 됨</p>
<h2 id="4-토큰-생성">4. 토큰 생성</h2>
<p>아이디: 깃허브 유저 아이디</p>
<p>패스워드: 토큰 (깃허브에서 발급해주는 토큰)</p>
<p>토큰 확인</p>
<p>깃허브 세팅 → developer setting → personal access tokens → tokens → 상단의 generate tokens </p>
<p><img src="https://velog.velcdn.com/images/dan_/post/39789096-4114-4805-a2ba-99fa1bb68526/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/dan_/post/fd4d18c2-e498-4c2b-9f59-1d208b9890bb/image.png" alt=""></p>
<p>용도는 마음대로</p>
<p>expiration은 최대한 no expiration은 말고</p>
<h2 id="5-cli-clone">5. CLI clone</h2>
<p>이전에 했던 작업(push)는 깃허브 레포에 올리는 거였다면,</p>
<p>이번엔 깃허브에 올라온 폴더를 로컬에 받아오는 작업 진행</p>
<h3 id="받아오기-vs-올리기">받아오기 vs 올리기</h3>
<p>소스코드 올리기 (로컬 → 깃허브)</p>
<ol>
<li><code>연결</code> → git remote add origin url</li>
<li><code>소스코드 올리기</code> → git push origin main</li>
</ol>
<p>소스코드 받아오기 (로컬 ← 깃허브)</p>
<ol>
<li><code>연결</code> → git clone</li>
<li><code>소스코드 받아오기</code> → git clone</li>
</ol>
<p>즉, git clone 명령어로 연결 + 소스코드 받아오기 한방에 가능</p>
<h3 id="git-clone">git clone</h3>
<pre><code class="language-markdown">git clone 원격저장소url</code></pre>
<p><img src="https://velog.velcdn.com/images/dan_/post/8f812a9c-6c89-43de-badd-04c862923c16/image.png" alt=""></p>
<p>연결된 것 확인 가능</p>
<p><img src="https://velog.velcdn.com/images/dan_/post/dee3a2b7-5a33-4020-9ffc-bef7f1c06654/image.png" alt=""></p>
<p>소스코드 받아온 것 확인 가능</p>
<h2 id="4-pull-하다-안되어서-git-remote-remove-origin">4. pull 하다 안되어서 git remote remove origin</h2>
<p><img src="https://velog.velcdn.com/images/dan_/post/6446c20d-c4c9-48fa-9068-1bd532522d93/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/dan_/post/af69a07f-692d-4a60-abc2-d5465f28c583/image.png" alt=""></p>
<p>받아올 소스코드가 변경되었는데, 현재 깃허브로부터 로컬에 받아온 소스코드는 이전 버전임</p>
<p>이럴 때, pull을 사용해서 수정된 소스코드 받아올 수 있음</p>
<p>git pull 명령어를 사용하니, 깃이 아니라는 오류 메시지가 발생하게 됨</p>
<p>이런 경우는 어떻게 해결할까?</p>
<pre><code class="language-markdown">cd devcourse</code></pre>
<p><img src="https://velog.velcdn.com/images/dan_/post/21810b0f-28f8-4b90-9abe-d83e0a841ac2/image.png" alt=""></p>
<ol>
<li>안쪽 폴더로 이동할 것 (gittestclone안에 pull해온 firstrepository(내건, devcourse)가 있음</li>
<li>git remote remove origin으로 연결 끊기</li>
<li>다시 gittestclone 폴더에서 init</li>
<li>init 후 git clone으로 연결해주기</li>
<li>그런데, git remote -v로 확인해보면, 연결된 레포가 없음…</li>
<li>왜 그 자체에는 연동 불가하고 하위 폴더로 만들어도 연동되는 걸까…?</li>
</ol>
<h2 id="5-gui로-clone하면-사실-맞게-했다는-것을-알게-됨">5. GUI로 clone하면 사실 맞게 했다는 것을 알게 됨…</h2>
<h3 id="왜-gui로">왜 GUI로?</h3>
<p>폴더 생성 후, 오픈한 상태에서 cli로 클론하게 되면, 상위 폴더와 하위폴더(클론된 폴더) 둘 다 한번에 보이게 됨. 포커싱이 클론된 폴더에만 있는 게 아님.</p>
<p>gui로 하게 되면, 클론한 폴더의 상위폴더는 vscode에 바로 나오는게 아니라서(물론 cli처럼, 폴더 안에 하위폴더로 클론된 거 맞음) 하위폴더(클론 폴더)에 포커싱이 되는 이점이 존재</p>
<h3 id="gui로-클론해보기">GUI로 클론해보기</h3>
<p>웰컴란에 clone git repository 존재(혹은 왼쪽 바의 소스 컨트롤 란에서)</p>
<p><img src="https://velog.velcdn.com/images/dan_/post/ed7a1c93-aec6-4872-8e9b-2183a93ece0b/image.png" alt=""></p>
<p>버튼 누르면 화면 상당에 Clone from github 나옴</p>
<p><img src="https://velog.velcdn.com/images/dan_/post/b1765e96-ea54-4666-86f4-9018f1e49780/image.png" alt=""></p>
<p>클론하기 원하는 레파지토리 url 입력하기</p>
<p>입력하고 나면, 로컬에서 원하는 폴더 선택하게 됨</p>
<p><img src="https://velog.velcdn.com/images/dan_/post/80815ff6-3766-482d-8c92-913c0aa06f63/image.png" alt=""></p>
<p>-&gt; CLI로 한 결과 (상위폴더까지 보임)</p>
<p><img src="https://velog.velcdn.com/images/dan_/post/59688da2-efce-4887-a982-abbc37741d9f/image.png" alt=""></p>
<p>-&gt; GUI로 한 결과 (하위폴더(=클론된 폴더)만 보임)</p>
<p>이 두 방법 알아야 하는 이유?</p>
<p>협업 시, 내 방법뿐만 아니라 팀원의 방법도 이해해야 하기 때문</p>
<h2 id="6-깃허브에-올린-프로젝트-내려받기">6. 깃허브에 올린 프로젝트 내려받기</h2>
<p>gittest, gittestclone 두 폴더 생성해서 깃허브 실습해보기</p>
<p>전자는 코드 업로드, 수정하는 폴더</p>
<p>후자는 깃헙에 올라간 폴더 다운받아보는 폴더</p>
<p>전자에서는 test.txt 파일 생성 후</p>
<pre><code class="language-markdown">git status #상태 체크
git add test.txt
git commit -m &quot;first commit&quot;
git remote -v #레포랑 연결 체크
git remote add origin 레포url
git push origin main</code></pre>
<p>다음의 순서로 test.txt 파일을 first commit이라는 커밋 메시지와 함께 레포에 업로드</p>
<p>후자에서는 레포에 올라간 gittest폴더를 내려받기 위해서</p>
<pre><code class="language-markdown">git status
git clone 레포url</code></pre>
<p>확인하면, 클론된 걸 볼 수 있음</p>
<p>로컬 폴더 안에서 깃헙 폴더가 클론되어 내려받으면, 클론된 폴더의 상위폴더는 git init도 안되어있기에, 상위폴더에서 깃작업하면 오류 발생</p>
<p>따라서, 추가적인 깃작업은 클론한 하위폴더로 이동 후 진행</p>
<h2 id="7-브랜치란">7. 브랜치란?</h2>
<p>branch == 가지</p>
<p>프로젝트가 한 개가 있었다면, 복사해서 사용하게 됨</p>
<p>용도에 따라서 프로젝트 따로따로 복사해 분리한 게 브랜치</p>
<h3 id="브랜치-생성-및-이동-명령어">브랜치 생성 및 이동 명령어</h3>
<ul>
<li>git status로 현재 깃 상태 확인</li>
</ul>
<pre><code class="language-markdown">git status</code></pre>
<ul>
<li>git branch로 브랜치 목록을 확인 가능</li>
</ul>
<pre><code class="language-markdown">git branch</code></pre>
<pre><code>(*)가 붙은 브랜치가 내가 현재 있는 브랜치</code></pre><ul>
<li><p>브랜치 생성</p>
<pre><code class="language-markdown">git branch 브랜치명</code></pre>
</li>
<li><p>브랜치 이동</p>
</li>
</ul>
<pre><code class="language-markdown">git checkout 브랜치명</code></pre>
<pre><code>지금 현재 브랜치에서 체크아웃해서 나가서 브랜치명으로 간다의 의미임</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[데브코스][1-2] Git 기본 이론]]></title>
            <link>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A41-2-Git-%EA%B8%B0%EB%B3%B8-%EC%9D%B4%EB%A1%A0</link>
            <guid>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A41-2-Git-%EA%B8%B0%EB%B3%B8-%EC%9D%B4%EB%A1%A0</guid>
            <pubDate>Wed, 10 Apr 2024 09:02:46 GMT</pubDate>
            <description><![CDATA[<h2 id="1-깃은-분산식-버전-관리-시스템">1. 깃은 분산식 버전 관리 시스템</h2>
<h3 id="깃-깃허브">깃, 깃허브</h3>
<p>깃은 로컬 시스템 (내 컴퓨터에 설치되는 시스템) 설치되는 프로그램</p>
<p>깃은 내 컴퓨터 안에서 버전을 관리해주는 시스템이자, 깃허브와 소통하는 시스템</p>
<p>깃허브는 원격으로 중앙에서 우리 프로젝트를 백업해두고, 버전 관리 해주고, 협업도 하게 해주는 시스템</p>
<p>깃허브는 원격 컴퓨터에서 백업을 담당하는 시스템이자, 내 컴퓨터와 별도로 버전 관리를 해주는 플랫폼</p>
<p>협업을 위해서는 깃허브라는 중간 장치가 있어야 하고 각각 개개인의 컴퓨터에는 깃이 설치되어야하는 것</p>
<h3 id="깃과-깃허브의-필요성">깃과 깃허브의 필요성</h3>
<p>하나의 프로젝트 내의 여러 폴더가 존재</p>
<p>한 폴더에서 유의미한 수정이 이루어진다? → 폴더 그리고 프로그램 모두 버전이 변화</p>
<p>만약 깃이 없다고 가정?</p>
<p>변화한 폴더 버전을 복붙 후, 이전 파일 저장, 새로 생성된 파일 수정…</p>
<p>무수히 늘어나는 버전들을 저장하려면 똑같은 거 계속 복붙해놓으면 메모리가 아까움</p>
<p>프로젝트 버전 관리를 굳이 컴퓨터에서 안 하고, 깃허브가 대신해주는 것</p>
<p>깃허브는 버전이 몇 개이든 다 가지고 있어줌, 무료로</p>
<p>사용하지 않는 이전 버전들을 깃허브에 올려두거나 현재 버전이 날아가는 것을 방지해 저장</p>
<p>현재 버전(1.0)을 깃허브에 올려두고, 수정을 하면 버전(1.1)이 변경됨(내 컴퓨터에서)</p>
<p>깃허브는 이전 버전과 현재 버전 모두 관리하며, 원할떄마다 버전별로 사용 가능하도록 해줌</p>
<p>깃허브 동기화 이후에는 작업 폴더를 다른 컴퓨터에서도 깃허브에서 다운받아서 사용 가능</p>
<h2 id="2-cli-vs-gui">2. CLI vs GUI</h2>
<h3 id="cli">CLI</h3>
<p>커멘드 라인 인터페이스</p>
<p>셀(명령 프롬프트; 창)에서 글자를 입력해서 컴퓨터에서 명령을 내리는 것</p>
<p>터미널, 윈도우 터미널 등은 특정 프로그램을 위해서 만든 것이 아니라 일반적으로 만들어놓은 집합체 같은 느낌</p>
<p>파워셀, 배쉬, cmd</p>
<h3 id="gui">GUI</h3>
<p>그래픽 유저 인터페이스</p>
<p>그래픽으로 컴퓨터에게 명령 내릴 수 있도록 사용자에게 화면 제공</p>
<h2 id="3-자주-쓰는-명령어-ls-cd-mkdir">3. 자주 쓰는 명령어 ls, cd, mkdir</h2>
<h3 id="ls">ls</h3>
<p>list segmentation의 약자</p>
<p>폴더 내의 요소들에 대해서 보여줌</p>
<p>폴더 내의 좀 더 많은 요소들을 전부 보여주는 ls -a</p>
<pre><code class="language-jsx">ls -a</code></pre>
<h3 id="cd">cd</h3>
<p>change directory의 약자</p>
<p>폴더 이동 명령어</p>
<p>명령어 뒤에 이동을 원하는 폴더명을 작성하면 이동됨</p>
<pre><code class="language-jsx">cd ..</code></pre>
<p>..는 상위 폴더</p>
<p>따라서 cd ..는 상위폴더로 이동하는 명령어</p>
<h3 id="mkdir">mkdir</h3>
<p>make directory의 약자</p>
<p>폴더 생성 명령어</p>
<p>명령어 뒤에 이름 기입하면</p>
<p>해당 이름의 폴더 생성됨</p>
<h2 id="4-자주-쓰는-명령어-init-status">4. 자주 쓰는 명령어 init, status</h2>
<h3 id="init">init</h3>
<p>깃아 너가 관리하는 저장소로 태어날게 초기화 시켜줘라는 의미</p>
<p>깃이 폴더 관리를 하기 위해서 폴더 안에 .git이라는 파일 생성(눈에 보이진 않게)</p>
<p>이후에 status를 확인하면, 아직 커밋이 없다고 하는 문구 확인 가능</p>
<p>초기화로 .git 폴더도 생성되었지만, 따로따로 그 안의 파일에 대해서도 깃에게 관리 요청을 해야 함</p>
<h3 id="status">status</h3>
<p>깃 상태를 보는 명령어</p>
<blockquote>
<p>⚠️ fatal: not a git repository (or any of the parent directories): .git</p>
</blockquote>
<p>깃에게 버전관리를 해달라고 요청하지 않으면 생기는 오류 문구</p>
<p>깃은 버전관리를 해주지만 컴퓨터에 있는 모든 폴더를 버전 관리하는 것은 불필요하기에 사용자가 요청하는 폴더 단위로 버전 관리를 진행 → 우리가 직접 폴더를 관리해달라고 요청해야 함</p>
<h2 id="5-자주-쓰는-명령어-add">5. 자주 쓰는 명령어 add</h2>
<p>깃은 시작부터 모든 파일을 다 트래킹해주지 않음</p>
<p>깃이 관리해주길 바란다면, <code>git add 파일명</code> 이라는 명령어를 사용해야 함</p>
<p>.git 폴더 안에 버전 관리 중인 상태를 적어놓음</p>
<p>성공적으로 수행되면, 아무런 멘트도 발생하지 않음</p>
<p>하지만, 버전을 저장하기 전에 관리하는, 더 저장할 건 없는지 기다리는 중</p>
<p>commit 전에 버전 만들기 위해서 파일 모으는 것</p>
<h2 id="6-자주-쓰는-명령어-commit-log">6. 자주 쓰는 명령어 commit, log</h2>
<h3 id="git-commit">git commit</h3>
<p>버전으로 저장할 것 모으고 있었지?</p>
<p>이제 add 아니고, 지금까지 모은 걸로 버전 만들자 </p>
<p>git commit 명령어를 통해서!</p>
<p>git commit은 add로 모은 것으로 버전 만드는 명령어(vim(텍스트 편집기)를 이용해서 커밋 명령어를 따로 작성해줘야 하는 번거로운 명령어로 git commit -m 명령어의 형태가 더 자주 쓰임)</p>
<blockquote>
<p>⚠️ vim 명령어</p>
</blockquote>
<p>vim에서는 <code>명령모드</code>, <code>입력모드</code>, <code>마지막 행 모드</code>로 총 3가지 모드가 존재</p>
<p><code>명령모드</code>는 처음 vim 모드 실행 들어가게 되는 곳</p>
<p>방향키로 이동 가능</p>
<p><code>입력모드</code>는 글 작성 가능하며, 다시 명령모드로 돌아오려면 esc를 누르면 됨</p>
<p><code>마지막 행 모드</code>는 명령모드에서 :를 입력 맨 밑단에 :___ 형태로 입력 가능한 공간 생성됨. 현재까지 작성한 내용 저장하고 vim 저장(wq)할지, 그냥 종료(q, q!)할지 입력 가능</p>
<pre><code class="language-jsx">git commit</code></pre>
<ul>
<li><p>cc로 변경(커서가 있는 그줄의 내용 변경)
<img src="https://velog.velcdn.com/images/dan_/post/86feb0da-0627-4d5a-a549-315eb151735b/image.png" alt=""></p>
</li>
<li><p>커밋 메시지 작성 후 esc(입력 모드에서 명령 모드로 변환)
<img src="https://velog.velcdn.com/images/dan_/post/b87e2086-a784-4dce-912e-5e01da0dc98e/image.png" alt=""></p>
</li>
<li><p>:wq(:는 명령모드에서 마지막 행 모드로 변환, wq는 저장 종류의 명령어)
<img src="https://velog.velcdn.com/images/dan_/post/7742b1c0-22f6-4116-8ac0-5d5d426fa619/image.png" alt=""></p>
</li>
</ul>
<h3 id="git-commit--m-명령어">git commit -m &quot;명령어&quot;</h3>
<pre><code class="language-jsx">git commit -m 커밋 메시지</code></pre>
<p>git commit보다 간결한 명령어</p>
<h3 id="git-log">git log</h3>
<p><img src="https://velog.velcdn.com/images/dan_/post/54280b76-45db-4486-a038-6dd4b581824b/image.png" alt=""></p>
<p>지난 commit 내용 확인 가능</p>
<p>커밋된 순서, 커밋 메시지, 해시 정보를 얻을 수 있음</p>
<h2 id="7-gui로-init-add-commit">7. GUI로 init, add, commit</h2>
<p>소스 컨트롤 란에서 gui로 init, add, commit 진행 가능</p>
<p>파일 작성 후, 소스 칸을 가면, U라는 마크 확인 가능</p>
<p>Untracked 즉, add가 되지 않은 상태라는 것</p>
<p><img src="https://velog.velcdn.com/images/dan_/post/3879f2c6-1da6-40fe-a4f7-b41b337d1618/image.png" alt=""></p>
<p>+를 통해서 add 가능</p>
<p>add가 되면, commit 버튼을 눌러서 커밋까지 완료해주면 됨</p>
<p>commit 버튼을 누르면 cli와 마찬가지로, 커밋메시지를 작성할 수 있음</p>
<h4 id="참고사항추가-파일-옆-마커들">참고사항(추가 파일 옆 마커들)</h4>
<pre><code> **A** - Added (This is a new file that has been added to the repository)

**M** - Modified (An existing file has been changed)

**D** - Deleted (a file has been deleted)

**U** - Untracked (The file is new or has been changed but has not been added to the repository yet)

**C** - Conflict (There is a conflict in the file)

**R** - Renamed (The file has been renamed)

**S** - Submodule (In repository exists another subrepository)

**T** - Typechange (The file changed from symlink to regular file, or visa versa)</code></pre><h2 id="8-깃-히스토리-설치">8. 깃 히스토리 설치</h2>
<p>extension에서 git history 검색 후 설치
<img src="https://velog.velcdn.com/images/dan_/post/22d17074-3a08-4dbb-a540-a92bf7d18df2/image.png" alt=""></p>
<p>소스에서 하단의 버튼 누르면 히스토리 확인 가능 </p>
<p><img src="https://velog.velcdn.com/images/dan_/post/5fb6f46c-d25c-44e3-9575-9d76686bcad6/image.png" alt=""></p>
<p>git log처럼 커밋 기록을 확인할 수 있음</p>
<p><img src="https://velog.velcdn.com/images/dan_/post/6ef41e0b-ec28-4a3e-a730-959890ba0adc/image.png" alt=""></p>
<p>커밋 메시지를 확인할 수 있고, 하단의 view를 통해서 버전명도 확인 가능</p>
<h2 id="9-git-remote--v-깃허브-소개">9. git remote -v, 깃허브 소개</h2>
<p>깃허브는 원격으로 중앙에서 우리 프로젝트 백업해두고 버전 관리해주고 협업도 하게 해주는 시스템!</p>
<p>우리는 대기업의 컴퓨터를 중앙에 두고 원격으로 사용하는 것</p>
<p>깃은 우리 컴퓨터 안에서 버전 관리하는 걸 넘어서 깃허브랑 연동도 가능함</p>
<p>자주 쓰이는 명령어 remote, 연동을 위해서는 remote라는 명령어를 사용해야 함</p>
<pre><code class="language-jsx">git remote -v </code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[데브코스][1-1] 프로젝트란? ]]></title>
            <link>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A41-1-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%9E%80</link>
            <guid>https://velog.io/@dan_/%EB%8D%B0%EB%B8%8C%EC%BD%94%EC%8A%A41-1-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%9E%80</guid>
            <pubDate>Tue, 09 Apr 2024 08:57:32 GMT</pubDate>
            <description><![CDATA[<h2 id="1-프로젝트-관리의-중요성">1. 프로젝트 관리의 중요성</h2>
<h3 id="프로젝트란">프로젝트란?</h3>
<p>일정 목적을 달성하기 위한 모든 일련의 프로세스</p>
<p>엄청 방대하고 거대한 곳 아님</p>
<p>마무리 되어야만 하는 것 아님</p>
<p>프로젝트는 우리가 범위를 설정하기 나름</p>
<p>기획, 설계 등 모든 단계가 필요함</p>
<h3 id="프로젝트-예시">프로젝트 예시</h3>
<p>계산기</p>
<p>노션</p>
<p>카톡</p>
<p>네이버</p>
<p>네이버라는 것도 거대한 프로젝트이지만, </p>
<p>네이버 안의 계산기를 만드는 것도 프로젝트임</p>
<h3 id="협업">협업</h3>
<p>협업은 공유하는 것</p>
<p>개인 프로젝트와 팀 프로젝트의 차이는 많이 사라졌지만, 회사 현업에서는 대부분 협업 진행</p>
<p>따라서 협업하는 것 중요</p>
<p>협업을 위해서는 협업 툴 사용 및 공유하는 것이 중요함</p>
<p>코드 공유, 문서 공유, 진행 척도 등등 공유의 중요성</p>
<h3 id="리드미">리드미</h3>
<p>리드미 역할</p>
<ol>
<li>완성된 프로그램의 설명서<ol>
<li>프로그램 사용법</li>
<li>레퍼런스</li>
</ol>
</li>
<li>구현 중인 프로젝트의 현황<ol>
<li>구현된 주요 기능</li>
<li>구현 현황</li>
<li>코드가 해결해야하는 문제 등</li>
</ol>
</li>
</ol>
<h2 id="2-리드미-작성법개념">2. 리드미 작성법(개념)</h2>
<p>리드미는 확장자가 마크다운인 파일 (<strong>m</strong>ark<strong>d</strong>own → md)</p>
<p>일반 .txt는 말 그대로 텍스트일 뿐 </p>
<p>전혀 특별한 꾸밈이 없음</p>
<p>글의 가독성을 위해서는 단조로운 형식의 텍스트보다는 글자 크기, 형태, 색 등을 변경할 수 있는 것이 중요함</p>
<p>다양한 환경과 사이트에서 제공</p>
<p>일반인들에게는 네이버 블로그 글쓰기 도구 같이 GUI 제공</p>
<h2 id="3-리드미-작성법실습">3. 리드미 작성법(실습)</h2>
<p>웹개발자들의 언어</p>
<p>리드미 상의 인용문 - 강조의 의미</p>
<p>마크다운 - 디스코드, 깃허브 등에서 모두 사용</p>
<p>특정 기술 학습하면, 문법 달달 외우지 말고 써보면서 익숙해지면서 체화해나가는 게 중요</p>
<h2 id="4-버전관리">4. 버전관리</h2>
<h3 id="버전이란">버전이란?</h3>
<p>유의미한 수정(추가되고, 풍성해진 코드)</p>
<p>기능 삭제, 기능 추가 등</p>
<h3 id="버전-네이밍">버전 네이밍</h3>
<p>1.0 &gt; 1.1 &gt; 2.0</p>
<p>1.0, 2.0이 메인 수정</p>
<p>1.1같은 건 좀 더 마이너한 수정</p>
<h3 id="관리란">관리란?</h3>
<p>버전이 잘 인덱싱이 되고 있나</p>
<p>다운그레이드, 업그레이드 등등</p>
<p>각 버전이 잘 정리되어있고 어떤 걸 의미하는지</p>
<p>원할 때 사용 가능하다면 관리가 잘 된 것</p>
<h2 id="5-버전-관리의-중요성">5. 버전 관리의 중요성</h2>
<p>처음부터 완벽한 코드 작성 불가능</p>
<p>짜놓은 코드를 고도화해서 다음 버전 만들고, 반복</p>
<p>모든 버전들은 나름대로 그 버전에서의 완성본</p>
<p>버전 1은 버전 1에서는 완성본인 것</p>
<h3 id="버전-관리-시스템">버전 관리 시스템</h3>
<p>버전 관리를 하기 위해서는 version control system 활용</p>
<ul>
<li>버전 관리</li>
<li>백업 복구</li>
<li>협업(선택)</li>
</ul>
<h2 id="6-버전-관리-시스템의-종류">6. 버전 관리 시스템의 종류</h2>
<h3 id="로컬-vcs">로컬 VCS</h3>
<p>단순하게 말 그대로 로컬에서 버전 관리</p>
<p>버전관리, 백업 복구 가능, but 협업 불가</p>
<p>다른 사람과 협업하기 위해 컴퓨터 연결한다든가하는 것 없음</p>
<p>여전히 혼자 수행</p>
<h3 id="중앙집중식-vcs">중앙집중식 VCS</h3>
<p>버전관리, 백업 복구, 협업 가능</p>
<p>중앙 서버에 버전 파일 모두 저장</p>
<p>로컬에서 중앙 서버에 저장된 파일 일부 가져와서 작업</p>
<p>네트워크, 서로 연결만 되어있고 소통만 가능하다면 중앙 서버를 통해 다른 컴퓨터와도 소통, 협업 가능</p>
<h3 id="분산-vcs">분산 VCS</h3>
<p>대표적인 시스템이 <code>깃</code>, mercurial, bazaar</p>
<p>버전 1의 시스템을 작업하고 싶다면 중앙집중식에서는 필요한 시스템만 가져와서 작업 진행</p>
<p>분산에서는 프로젝트 통째로 그대로 들고와서 작업함</p>
<p>파일 하나만 가져오면 시스템 꼬일 수 있는데 폴더 통째로 작업한다면, 꼬이기 쉽지 않고 꼬이더라도 수정이 용이함</p>
<p>꼬인다는 것?</p>
<p>A, B 컴퓨터 동시에 같은 파일을 가지고 작업</p>
<p>A 컴퓨터가 파일 작업 후 저장, B 컴퓨터가 이후 작업하려고 할 때, 복잡함</p>
<p>파일이 1개가 아니라 몇십개씩 된다면, 너무 복잡함</p>
<p>단위가 좀 더 크게 폴더, 프로젝트 단위로 관리 포인트를 줄어들 수 있게 관리</p>
]]></description>
        </item>
    </channel>
</rss>