<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>florence_y.log</title>
        <link>https://velog.io/</link>
        <description>실패에 무딘 사람. 프론트엔드 개발자를 꿈꿉니다</description>
        <lastBuildDate>Tue, 09 Sep 2025 12:54:49 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>florence_y.log</title>
            <url>https://velog.velcdn.com/images/florence_y/profile/ef84125c-91ab-4d80-921c-440b29434192/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. florence_y.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/florence_y" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[TIL]NextAuth.js에서 Auth.js로 마이그레이션 하기(v4 → v5)]]></title>
            <link>https://velog.io/@florence_y/TILNextAuth.js%EC%97%90%EC%84%9C-Auth.js%EB%A1%9C-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98-%ED%95%98%EA%B8%B0with-Adapter</link>
            <guid>https://velog.io/@florence_y/TILNextAuth.js%EC%97%90%EC%84%9C-Auth.js%EB%A1%9C-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98-%ED%95%98%EA%B8%B0with-Adapter</guid>
            <pubDate>Tue, 09 Sep 2025 12:54:49 GMT</pubDate>
            <description><![CDATA[<h1 id="nextauthjs-v4-→-authjs-v5">NextAuth.js v4 → Auth.js v5</h1>
<p>Nex.js의 인증 라이브러리였던 <strong>NextAuth.js</strong>가 모든 JavaScript 프레임워크에서 사용할 수 있는 <strong>Auth.js</strong>로 확장되었다. </p>
<p>이번에 기존 Next.js 프로젝트를 마이그레이션하면서 인증 기능도 함께 Auth.js로 마이그레이션 해보았다.</p>
<h3 id="authjs-설치">auth.js 설치</h3>
<p><code>npm uninstall next-auth</code>
<code>npm install next-auth@beta</code></p>
<h3 id="adapter-설치">Adapter 설치</h3>
<p>데이터베이스 어댑터를 사용하고 있는 경우 새로 설치한다.</p>
<p><code>npm uninstall @next-auth/prisma-adapter</code>
<code>npm install @auth/prisma-adapter</code></p>
<h3 id="환경변수-변경">환경변수 변경</h3>
<p><code>.env</code> 파일의 변수명을 바꿔준다.</p>
<pre><code class="language-diff">// 기존
- GOOGLE_CLIENT_ID= &#39;...&#39;
- GOOGLE_CLIENT_SECRET= &#39;...&#39;
- NEXTAUTH_URL=http://localhost:3000

// 변경
+ AUTH_GOOGLE_ID= &#39;...&#39;
+ AUTH_GOOGLE_SECRET= &#39;...&#39;
+ AUTH_URL=&#39;http://localhost:3000&#39;</code></pre>
<h3 id="구성-파일-변경">구성 파일 변경</h3>
<p>기존의 <code>app/api/auth/[...nextauth]/route.ts</code>의 설정을 루트 위치로 옮긴다. 기존 파일은 <strong>App Router의 라우트 핸들러</strong>로 사용된다. </p>
<p>이때 <strong>데이터베이스 어댑터</strong>를 사용하는 경우 한 파일에서 관리했던 설정을 <strong>두 개의 파일</strong>로 나눈다.</p>
<h4 id="🤔-왜-두-개의-파일로-나눠야-할까">🤔 왜 두 개의 파일로 나눠야 할까?</h4>
<blockquote>
<p>💡 <strong>Auth.js는 두 가지 세션 전략을 지원한다.</strong> <br></p>
</blockquote>
<ol>
<li><strong>Database 세션</strong>: 데이터베이스에 사용자 정보를 저장해서 기억하는 방식</li>
<li><strong>JWT 세션</strong>: 사용자 정보 대신 암호화된 토큰을 주고받으면서 기억하는 방식. 데이터베이스는 거치지 않음</li>
</ol>
<p>데이터베이스 어댑터를 사용하는 경우 Database 세션 전략이 적용된다. 하지만 <strong>데이터베이스 어댑터가 Edge 런타임과 호환되지 않으면 데이터베이스에 접근할 수 없다.</strong> 
Edge 런타임은 사용자와 가까운 서버에서 코드를 실행시켜 응답 속도를 높이는 환경인데, 데이터베이스에 직접 연결할 수는 없다.</p>
<p>따라서 설정 파일을 나누어 <strong>Edge 런타임 같은 특정 환경에서는 JWT 세션 관련 설정만 적용</strong>되도록 만들고, 다른 환경에서는 <strong>기존 Database 세션을 사용</strong>할 수 있도록 만들어야 한다.</p>
<h4 id="기존-nextauthjs-v4">기존 NextAuth.js (v4)</h4>
<pre><code class="language-js">// app/api/auth/[...nextauth]/route.js

import NextAuth from &quot;next-auth&quot;;
import { prisma } from &quot;@/lib/prisma&quot;;
import { PrismaAdapter } from &quot;@next-auth/prisma-adapter&quot;;
import CredentialsProvider from &quot;next-auth/providers/credentials&quot;;
import bcrypt from &quot;bcrypt&quot;;
import GoogleProvider from &quot;next-auth/providers/google&quot;;
import KakaoProvider from &quot;next-auth/providers/kakao&quot;;
import NaverProvider from &quot;next-auth/providers/naver&quot;;

export const authOptions = {
    adapter: PrismaAdapter(prisma),
    providers: [
        CredentialsProvider({
            id: &quot;credentials&quot;,
            name: &quot;Credentials&quot;,
            credentials: {
                email: { label: &quot;Email&quot;, type: &quot;text&quot;, placehoder: &quot;이메일&quot; },
                password: {
                    label: &quot;Password&quot;,
                    type: &quot;password&quot;,
                    placehoder: &quot;비밀번호&quot;,
                },
            },

            async authorize(credentials, req) {
                if (!credentials.email) {
                    throw new Error(&quot;아이디를 입력해주세요.&quot;);
                } else if (!credentials.password) {
                    throw new Error(&quot;비밀번호를 입력해주세요.&quot;);
                }

                try {
                    let user = await prisma.user.findUnique({
                        where: {
                            email: credentials.email,
                        },
                    });

                    if (!user) {
                        throw new Error(
                            &quot;이메일 또는 비밀번호가 일치하지 않습니다.&quot;
                        );
                    }

                    const pwCheck = await bcrypt.compare(
                        credentials.password,
                        user.password
                    );

                    if (!pwCheck) {
                        throw new Error(
                            &quot;이메일 또는 비밀번호가 일치하지 않습니다.&quot;
                        );
                    }
                    return user;
                } catch (err) {
                    console.log(err);
                    // try 문에서 전달된 에러 처리
                    if (
                        err.message ==
                        &quot;이메일 또는 비밀번호가 일치하지 않습니다.&quot;
                    ) {
                        throw err;
                    }

                    // 예상치 못한 에러 처리
                    throw Error(
                        &quot;로그인 요청 중 문제가 발생했습니다. 잠시 후 다시 시도해주세요.&quot;
                    );
                }
            },
        }),
        KakaoProvider({
            clientId: process.env.KAKAO_CLIENT_ID,
            clientSecret: process.env.KAKAO_CLIENT_SECRET,
        }),
        NaverProvider({
            clientId: process.env.NAVER_CLIENT_ID,
            clientSecret: process.env.NAVER_CLIENT_SECRET,
        }),
        GoogleProvider({
            clientId: process.env.GOOGLE_CLIENT_ID,
            clientSecret: process.env.GOOGLE_CLIENT_SECRET,
        }),
    ],
    session: {
        strategy: &quot;jwt&quot;,
    },
    callbacks: {
        jwt: async ({ token, user }) =&gt; {
            if (user) {
                token.sub = user.id;
            }
            return token;
        },
        session: async ({ session, token }) =&gt; ({
            ...session,
            user: {
                ...session.user,
                id: token.sub,
            },
        }),
    },
    pages: {
        signIn: &quot;/login&quot;,
    },
    secret: process.env.SECRET,
};

const handler = NextAuth(authOptions);

export { handler as GET, handler as POST };</code></pre>
<h4 id="authjs-v5">Auth.js (v5)</h4>
<p><strong><code>auth.config.ts</code></strong>:  JWT 세션 관련 설정 파일. Edge 런타임에도 안전하다.</p>
<pre><code class="language-ts">// auth.config.ts

import type { NextAuthConfig } from &quot;next-auth&quot;;
import Google from &quot;next-auth/providers/google&quot;;
import Kakao from &quot;next-auth/providers/kakao&quot;;
import Naver from &quot;next-auth/providers/naver&quot;;

export const authConfig = {
    providers: [
        Google({
            clientId: process.env.AUTH_GOOGLE_ID!,
            clientSecret: process.env.AUTH_GOOGLE_SECRET!,
        }),
        Kakao({
            clientId: process.env.AUTH_KAKAO_ID!,
            clientSecret: process.env.AUTH_KAKAO_SECRET!,
        }),
        Naver({
            clientId: process.env.AUTH_NAVER_ID!,
            clientSecret: process.env.AUTH_NAVER_SECRET!,
        }),
    ],
    pages: {
        signIn: &quot;/login&quot;,
    },
} satisfies NextAuthConfig;</code></pre>
<p><strong><code>auth.ts</code></strong>:  Database 세션 관련 설정 파일. <code>auth.config.ts</code> 파일을 가져와 필요한 설정을 추가한다.</p>
<pre><code class="language-ts">// auth.ts

import { prisma } from &quot;@/lib/prisma&quot;;
import { PrismaAdapter } from &quot;@auth/prisma-adapter&quot;;
import NextAuth from &quot;next-auth&quot;;
import { authConfig } from &quot;./auth.config&quot;;
import Credentials from &quot;next-auth/providers/credentials&quot;;
import bcrypt from &quot;bcryptjs&quot;;

export const { auth, handlers, signIn, signOut } = NextAuth({
    ...authConfig, // auth.config.ts에서 정의한 기본 설정 가져옴
    adapter: PrismaAdapter(prisma),
    session: {
        strategy: &quot;jwt&quot;,
        maxAge: 60 * 60 * 24,
    },
    providers: [
        ...authConfig.providers, // auth.config.ts에 정의된 providers 가져옴
        Credentials({
            name: &quot;Credentials&quot;,
            credentials: {
                email: { label: &quot;Email&quot;, type: &quot;text&quot; },
                password: { label: &quot;Password&quot;, type: &quot;password&quot; },
            },
            authorize: async (credentials, req) =&gt; {
                const { email, password } = credentials;

                try {
                    let user = await prisma.user.findUnique({
                        where: {
                            email: email as string,
                        },
                    });

                    // 유저가 없는 경우 에러 처리
                    if (!user) {
                        console.log(&quot;유저 정보 없음&quot;);
                        return null;
                    }

                    const pwCheck = await bcrypt.compare(
                        password as string,
                        user.password!
                    );

                    // 비밀번호가 일치하지 않는 경우 에러 처리
                    if (!pwCheck) {
                        console.log(&quot;비밀번호 불일치&quot;);
                        return null;
                    }

                    return {
                        id: user.id,
                        email: user.email || undefined,
                        name: user.name || null,
                    };
                } catch (err) {
                    // 예상치 못한 에러 처리
                    console.error(&quot;Authorize Error:&quot;, err);
                    return null;
                }
            },
        }),
    ],
    callbacks: {
        signIn: async () =&gt; {
            return true;
        },
        jwt: async ({ token, user }) =&gt; {
            if (user) {
                token.id = user.id;
                token.email = user.email;
            }
            return token;
        },
        session: async ({ session, token }) =&gt; {
            if (session &amp;&amp; token) {
                session.user.id = token.id as string;
                session.user.email = token.email as string;
            }
            return session;
        },
    },
});</code></pre>
<p><code>app/api/auth/[...nextauth]/route.ts</code> 파일은 라우트 핸들러로 대체된다.</p>
<pre><code class="language-ts">// app/api/auth/[...nextauth]/route.ts

import { handlers } from &quot;@/auth&quot;;

export const { GET, POST } = handlers;</code></pre>
<h3 id="서버-컴포넌트">서버 컴포넌트</h3>
<h4 id="기존-nextauthjs-v4-1">기존 NextAuth.js (v4)</h4>
<p><code>getServerSession</code>을 사용하여 세션을 가져왔다.</p>
<pre><code class="language-js">import { getServerSession } from &quot;next-auth&quot;;
import { authOptions } from &quot;../auth/[...nextauth]/route&quot;;

export async function POST(req) {
    const session = await getServerSession(authOptions);

  // 코드
}</code></pre>
<h4 id="authjs-v5-1">Auth.js (v5)</h4>
<p>Auth.js에서 제공하는 <code>auth()</code> 함수를 호출하여 세션을 가져온다.</p>
<pre><code class="language-ts">// auth.ts

export const { auth, handlers, signIn, signOut } = NextAuth({
  // 코드
})</code></pre>
<pre><code class="language-ts">import { auth } from &quot;@/auth&quot;;

export async function POST(req: NextRequest) {
    const session = await auth();  

  // 코드
}</code></pre>
<h3 id="미들웨어">미들웨어</h3>
<h4 id="기존-nextauthjs-v4-2">기존 NextAuth.js (v4)</h4>
<p>기존에는 <code>next-auth/jwt</code>의 <code>getToken</code>을 사용해 사용자의 세션을 확인했다.</p>
<pre><code class="language-js">// middleware.js

import { getToken } from &quot;next-auth/jwt&quot;;
import { NextResponse } from &quot;next/server&quot;;

const secret = process.env.SECRET;

export async function middleware(req) {
    const token = await getToken({ req, secret });
    const { pathname } = req.nextUrl;

    if (pathname.startsWith(&quot;/login&quot;) || pathname.startsWith(&quot;/signup&quot;)) {
        if (token) {
            // ...
        }
    } 
}</code></pre>
<h4 id="authjs-v5-2">Auth.js (v5)</h4>
<p>Auth.js의 <code>auth()</code> 함수를 사용해 사용자의 세션을 확인한다. 미들웨어는 Edge 런타임에서 실행되기 때문에 <code>auth.config.ts</code> 파일의 설정을 가져와 필요한 인증만 가볍게 처리한다.</p>
<pre><code class="language-ts">// middleware.ts

import { NextResponse } from &quot;next/server&quot;;
import { authConfig } from &quot;./auth.config&quot;;
import NextAuth from &quot;next-auth&quot;;

const { auth } = NextAuth(authConfig);

export default auth(async function middleware(req) {
    const { pathname } = req.nextUrl;

    if (pathname.startsWith(&quot;/login&quot;) || pathname.startsWith(&quot;/signup&quot;)) {
        if (req.auth?.user) {
            // ...
        }
    }
});</code></pre>
<h2 id="정리">정리</h2>
<ul>
<li>환경변수 <code>NEXTAUTH_</code> → <code>AUTH_</code>로 변경</li>
<li><code>app/api/auth/[...nextauth]/route.ts</code> 구조 변경</li>
<li>JWT 세션 관련 설정 파일과 Database 세션 관련 설정 파일 분리</li>
<li>서버 컴포넌트에서 <code>getServerSession</code> → <code>auth()</code>로 세션 접근</li>
</ul>
<br>
<br>
<br>


<p>참고</p>
<p><a href="https://authjs.dev/getting-started/migrating-to-v5">https://authjs.dev/getting-started/migrating-to-v5</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Clean Code]클린코드_3. 함수]]></title>
            <link>https://velog.io/@florence_y/Clean-Code%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C3.-%ED%95%A8%EC%88%98</link>
            <guid>https://velog.io/@florence_y/Clean-Code%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C3.-%ED%95%A8%EC%88%98</guid>
            <pubDate>Wed, 16 Oct 2024 01:37:19 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-함수">📌 함수</h1>
<h3 id="작게-만들어라">작게 만들어라!</h3>
<ul>
<li>함수를 만드는 규칙은 &#39;작게!&#39;다! 함수를 만드는 둘째 규칙은 &#39;더 작게!&#39;다.</li>
<li>if 문/else 문/while 문 등에 들어가는 블록은 한 줄이어야 한다는 의미다. </li>
<li>중첩 구조가 생길 만큼 함수가 커져서는 안 된다는 뜻이다.</li>
<li>함수에서 들여쓰기 수준은 1단이나 2단을 넘어서면 안 된다.</li>
</ul>
<h3 id="한-가지만-해라">한 가지만 해라!</h3>
<ul>
<li><strong>함수는 한 가지를 해야 한다. 그 한 가지를 잘 해야 한다. 그 한 가지만을 해야 한다.</strong></li>
<li>지정된 함수 이름 아래에서 추상화 수준이 하나인 단계만 수행한다면 그 함수는 한 가지 작업만 한다.</li>
<li>단순히 다른 표현이 아니라 의미 있는 이름으로 다른 함수를 추출할 수 있다면 그 함수는 여러 작업을 하는 셈이다. </li>
</ul>
<h3 id="함수당-추상화-수준은-하나로">함수당 추상화 수준은 하나로!</h3>
<ul>
<li>함수 내 모든 문장의 추상화 수준이 동일해야 한다.</li>
</ul>
<h4 id="위에서-아래로-코드-읽기-내려가기-규칙">위에서 아래로 코드 읽기: 내려가기 규칙</h4>
<ul>
<li>위에서 아래로 프로그램을 읽으면 함수 추상화 수준이 한 번에 한 단계씩 낮아진다. </li>
</ul>
<h3 id="switch-문">Switch 문</h3>
<ul>
<li>본질적으로 switch 문은 N 가지를 처리한다. 각 switch 문을 저차원 클래스에 숨기고 절대로 반복하지 않는 방법은 있다. 물론 다형성을 이용한다.</li>
</ul>
<h3 id="서술적인-이름을-사용하라">서술적인 이름을 사용하라!</h3>
<ul>
<li>함수가 작고 단순할수록 서술적인 이름을 고르기도 쉬워진다.</li>
<li>함수 이름을 정할 때는 여러 단어가 쉽게 읽히는 명명법을 사용한다. </li>
<li>그다음, 여러 단어를 사용해 함수 기능을 잘 표현하는 이름을 선택한다.</li>
<li>이름을 붙일 때는 일관성이 있어야 한다. </li>
<li>모듈 내에서 함수 이름은 같은 문구, 명사, 동사를 사용한다.</li>
</ul>
<h3 id="함수-인수">함수 인수</h3>
<ul>
<li>함수에서 이상적인 인수 개수는 0개(무항)다. 다음은 1개(단항)고, 다음은 2개(이항)다.</li>
</ul>
<h4 id="많이-쓰는-단항-형식">많이 쓰는 단항 형식</h4>
<ul>
<li>함수에 인수 1개를 넘기는 이유로 가장 흔한 경우는 두 가지다. 하나는 인수에 질문을 던지는 경우다. 다른 하나는 인수를 뭔가로 변환해 결과를 반환하는 경우다.</li>
<li>아주 유용한 단항 함수 형식이 이벤트다.</li>
</ul>
<h4 id="이항-함수">이항 함수</h4>
<ul>
<li>인수가 2개인 함수는 인수가 1개인 함수보다 이해하기 어렵다.</li>
<li>이항 함수가 무조건 나쁘다는 소리는 아니다. 하지만 그만큼 위험이 따른다는 사실을 이해하고 가능하면 단항 함수로 바꾸도록 애써야 한다.</li>
</ul>
<h4 id="삼항-함수">삼항 함수</h4>
<ul>
<li>삼항 함수를 만들 때는 신중히 고려하라 권고한다. </li>
</ul>
<h4 id="인수-객체">인수 객체</h4>
<ul>
<li>인수가 2-3개 필요하다면 일부를 독자적인 클래스 변수로 선언할 가능성을 짚어본다.</li>
</ul>
<h4 id="동사와-키워드">동사와 키워드</h4>
<ul>
<li>단항 함수는 함수와 인수가 동사/명사 쌍을 이뤄야 한다.</li>
<li>write(name) → writeField(name)</li>
</ul>
<h3 id="부수-효과를-일으키지-마라">부수 효과를 일으키지 마라!</h3>
<ul>
<li>부수 효과는 거짓말이다. 함수에서 한 가지를 하겠다고 약속하고선 남몰래 다른 짓도 하니까.</li>
</ul>
<h4 id="출력-인수">출력 인수</h4>
<ul>
<li>일반적으로 우리는 인수를 함수 입력으로 해석한다.</li>
<li>객체 지향 언어에서는 출력 인수를 사용할 필요가 거의 없다. 출력 인수로 사용하라고 설계한 변수가 바로 this이기 때문이다.</li>
</ul>
<h3 id="명령과-조회를-분리하라">명령과 조회를 분리하라!</h3>
<ul>
<li>함수는 뭔가를 수행하거나 뭔가에 답하거나 둘 중 하나만 해야 한다.</li>
<li>객체 상태를 변경하거나 아니면 객체 정보를 반환하거나 둘 중 하나다.</li>
</ul>
<h3 id="오류-코드보다-예외를-사용하라">오류 코드보다 예외를 사용하라!</h3>
<ul>
<li>명령 함수에서 오류 코드를 반환하는 방식은 명령/조회 분리 규칙을 미묘하게 위반한다.</li>
<li>오류 코드 대신 예외를 사용하면 오류 처리 코드가 원래 코드에서 분리되므로 코드가 깔끔해진다.</li>
</ul>
<h3 id="trycatch-블록-뽑아내기">Try/Catch 블록 뽑아내기</h3>
<ul>
<li>try/catch 블록을 별도 함수로 뽑아내는 편이 좋다.</li>
<li>정상 동작과 오류 처리 동작을 분리하면 코드를 이해하고 수정하기 쉬워진다.</li>
</ul>
<h4 id="오류-처리도-한-가지-작업이다">오류 처리도 한 가지 작업이다.</h4>
<ul>
<li>오류를 처리하는 함수는 오류만 처리해야 마땅하다.</li>
<li>함수에 키워드 try가 있다면 함수는 try 문으로 시작해 catch/finally 문으로 끝나야 한다는 말이다.</li>
</ul>
<h3 id="반복하지-마라">반복하지 마라!</h3>
<ul>
<li>많은 원칙과 기법이 중복을 없애거나 제어할 목적으로 나왔다.</li>
<li>객체 지향 프로그래밍은 코드를 부모 클래스로 몰아 중복을 없앤다.</li>
</ul>
<h3 id="구조적-프로그래밍">구조적 프로그래밍</h3>
<ul>
<li>함수는 return 문이 하나여야 한다.</li>
<li>함수를 작게 만든다면 간혹 return, break, continue를 여러 차례 사용해도 괜찮다.</li>
</ul>
<h3 id="함수를-어떻게-짜죠">함수를 어떻게 짜죠?</h3>
<ul>
<li><strong>소프트웨어를 짜는 행위는 여느 글짓기와 비슷하다. 먼저 생각을 기록한 후 읽기 좋게 다듬는다.</strong></li>
<li>처음에는 길고 복잡하다. 들여쓰기 단계도 많고 중복된 루프도 많다. 인수 목록도 아주 길다. 이름은 즉흥적이고 코드는 중복된다. 하지만 그 서투른 코드를 빠짐없이 테스트하는 단위 테스트 케이스도 만든다.</li>
<li>그런 다음 코드를 다듬고, 함수를 만들고, 이름을 바꾸고, 중복을 제거한다. 메서드를 줄이고 순서를 바꾼다. 때로는 전체 클래스를 쪼개기도 한다.</li>
<li>처음부터 탁 짜내지 않는다. 그게 가능한 사람은 없으리라.</li>
</ul>
<h3 id="결론">결론</h3>
<ul>
<li>함수는 언어에서 동사며, 클래스는 명사다.</li>
<li><strong>대가(master) 프로그래머는 시스템을 (구현할) 프로그램이 아니라 (풀어갈) 이야기로 여긴다.</strong></li>
<li>프로그래밍 언어라는 수단을 사용해 좀 더 풍부하고 좀 더 표현력이 강한 언어를 만들어 이야기를 풀어간다.</li>
<li>여러분이 작성하는 함수가 분명하고 정확한 언어로 깔끔하게 같이 맞아떨어져야 이야기를 풀어가기가 쉬워진다는 사실을 기억하기 바란다.</li>
</ul>
<br>

<hr>
<p>📘 나는 그동안 함수 만들기의 가장 기본인 &#39;한 가지만 하는가&#39;를 잘 지켰는지 돌아보게 되었다. 이번 3장에서 가장 와닿았던 부분은 <strong>&#39;시스템을 구현하는 것이 아니라 풀어가야 하는 것&#39;</strong>이다. 무언가를 풀려고 하기보다는 빨리 완성시키기 위해 기능이 구현되면 바로 다음 작업으로 넘어가는 일이 다반사였다. 글짓기처럼 함수를 만들 때도 여러 번의 수정 작업이 필요하다는 것을 알게 되었다. 깔끔하고 좋은 함수를 짜기 위해 여러 번의 수정과 개선을 거치는 과정이 필수적임을 깨달았다. 단순히 기능을 구현하는 것에 그치지 않고, 더 나은 구조와 가독성을 위해 함수를 지속적으로 다듬어야 한다. 기한이 있는 프로젝트에서는 이 부분을 어떻게 적용시킬 수 있을지 고민해 봐야겠다. 
<br>
<br></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL]TypeScript_TypeScript란?]]></title>
            <link>https://velog.io/@florence_y/TILTypeScriptTypeScript%EB%9E%80</link>
            <guid>https://velog.io/@florence_y/TILTypeScriptTypeScript%EB%9E%80</guid>
            <pubDate>Fri, 28 Jun 2024 14:11:50 GMT</pubDate>
            <description><![CDATA[<p>이전까지 공부한 TypeScript 정리 겸 플러스 과정 수업 내용 정리하기!</p>
<h1 id="🔎-typescript">🔎 TypeScript</h1>
<h2 id="typescript란">TypeScript란?</h2>
<p>Microsoft에서 개발한 JavaScript에 타입을 붙인 언어로, JavaScript 기반의 강형(strongly typed programming language) 언어이다. </p>
<blockquote>
<h4 id="📌강형-언어-vs-약형-언어">📌강형 언어 VS 약형 언어</h4>
<p>** 강형 언어(strongly typed programming language)**
정의된 모든 상수 또는 변수에 데이터 타입이 선언되어야 하는 프로그래밍 언어. 타입 변환이 불가하다. 강형 언어는 컴파일러를 사용하는데, 문법 체크 혹은 타입 체크를 컴파일 단계에서 실행시킨다. 오류 발생 시 재빠르게 확인, 수정이 가능하다.
C++, Java, Python, TypeScript 등이 강형 언어이다.<br>
<strong>약형 언어(weakly typed programming language)</strong>
상수 또는 변수에 데이터 타입이 선언되지 않아 오류 발생 시 런타임 시점에 발생한다. 타입 변환이 가능하다.
JavaScript, PHP 등이 약형 언어이다.</p>
</blockquote>
<p>TypeScript로 작성된 코드는 TypeScript Complier(tsc)를 이용해 JavaScript로 컴파일된다.</p>
<pre><code class="language-ts">// ts코드
function sum(a: number, b: number): number {
  return a + b;
}
sum(10, 20);</code></pre>
<pre><code class="language-js">// js코드
function sum(a, b) {
  return a + b;
}
sum(10, 20);</code></pre>
<h2 id="typescript의-장점">TypeScript의 장점</h2>
<ul>
<li><p><strong>에러를 사전에 검출해 준다.</strong> JavaScript를 사용한 프로젝트의 개발 단계에서 오류를 발견하지 못하고 배포했을 경우, 사용자에 의해 에러를 발견하는 위험한 상황이 발생할 수도 있다. TypeScript는 개발 단계에서 오류를 발견하여 즉각적인 수정이 가능하다.</p>
</li>
<li><p><strong>코드 가이드 및 자동 완성 기능이 있다.</strong> 변수나 상수에 타입을 정확하게 명시해 주었기 때문에 타입에 맞는 메소드를 자동 완성 해주어 코드의 힌트를 얻을 수 있다.
<img src="https://velog.velcdn.com/images/florence_y/post/8a69fdda-be53-4d15-a27c-de8318c2bda1/image.jpg" alt=""></p>
</li>
</ul>
<h2 id="typescript-compilertsc">TypeScript Compiler(tsc)</h2>
<p>TypeScript 소스 코드를 브라우저에서 실행시키기 위해서는 JavaScript 소스 코드로 변환시켜 주어야 한다. TypeScript 소스 코드를 읽어 검증하고 JavaScript 소스 코드로 변환하는 작업을 TypeScript 컴파일러(tsc)를 통해 해준다.
TypeScript 컴파일 단계에서 TypeScript의 소스코드를 JavaScript 소스코드로 변환시켜 주고, 타입 관련 구문을 제거해 준다. 또한 브라우저 호환성을 위해 구 버전의 JavaScript로 변환해 준다.</p>
<h4 id="typescript-compilertsc-설치">TypeScript Compiler(tsc) 설치</h4>
<p><code>npm i typescript -g</code></p>
<h4 id="컴파일-실행">컴파일 실행</h4>
<p><code>tsc 파일명.ts</code></p>
<h4 id="실행">실행</h4>
<p><code>node 파일명.js</code></p>
<pre><code class="language-ts">// ts 컴파일 전
(() =&gt; {
    function hello(name: string) {
        return &quot;Hello&quot; + name;
    }
    console.log(hello(&quot;Yeo&quot;));
})();</code></pre>
<pre><code class="language-js">// ts 컴파일 후
(() =&gt; {
    function hello(name) {
        return &quot;Hello&quot; + name;
    }
    console.log(hello(&quot;Yeo&quot;));
})();</code></pre>
<h2 id="정리">정리</h2>
<ul>
<li>TypeScript란 JavaScript에 타입을 붙인 언어이다.</li>
<li>TypeScript Complier(tsc)를 이용해 JavaScript로 컴파일된다.</li>
<li>에러를 컴파일 단계에서 검출해 준다. </li>
<li>타입에 맞는 코드 가이드 및 자동 완성 기능이 있다. </li>
</ul>
<br>
<br>
<br>
참고

<p><a href="https://www.techtarget.com/whatis/definition/strongly-typed">https://www.techtarget.com/whatis/definition/strongly-typed</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Clean Code]클린코드_2. 의미 있는 이름]]></title>
            <link>https://velog.io/@florence_y/Clean-Code%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C2.-%EC%9D%98%EB%AF%B8-%EC%9E%88%EB%8A%94-%EC%9D%B4%EB%A6%84</link>
            <guid>https://velog.io/@florence_y/Clean-Code%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C2.-%EC%9D%98%EB%AF%B8-%EC%9E%88%EB%8A%94-%EC%9D%B4%EB%A6%84</guid>
            <pubDate>Wed, 19 Jun 2024 02:31:55 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-의미-있는-이름">📌 의미 있는 이름</h1>
<h3 id="의도를-분명히-밝혀라">의도를 분명히 밝혀라</h3>
<ul>
<li><strong>&quot;의도가 분명하게 이름을 지으라&quot;</strong></li>
<li>변수나 함수 그리고 클래스 이름은 다음과 같은 질문에 모두 답해야 한다.
→ 변수(혹은 함수나 클래스)의 존재 이유는?
→ 수행 기능은?
→ 사용 방법은?
→ 따로 주석이 필요하다면 의도를 분명히 드러내지 못했다는 말이다.</li>
<li>코드 맥락이 코드 자체에 명시적으로 드러나야 한다.</li>
</ul>
<h3 id="그릇된-정보를-피하라">그릇된 정보를 피하라</h3>
<ul>
<li>그릇된 단서는 코드 의미를 흐린다.</li>
<li>나름대로 널리 쓰이는 의미가 있는 단어를 다른 의미로 사용해도 안 된다.</li>
<li>일관성이 떨어지는 표기법은 그릇된 정보다. </li>
</ul>
<h3 id="의미-있게-구분하라">의미 있게 구분하라</h3>
<ul>
<li>동일한 범위 안에서는 다른 두 개념에 같은 이름을 사용하지 못한다. 그래서 프로그래머가 한쪽 이름을 마음대로 바꾸고픈 유혹에 빠진다.</li>
<li>컴파일러를 통과할지라도 연속된 숫자를 덧붙이거나 불용어를 추가하는 방식은 적절하지 못하다.</li>
<li>이름이 달라야 한다면 의미도 달라져야 한다.</li>
<li>불용어를 추가한 이름 역시 아무런 정보도 제공하지 못한다. </li>
<li>Product라는 클래스가 있는데 다른 클래스 이름을 ProductInfo 혹은 ProductData라 부른다면 개념을 구분하지 않은 채 이름만 달리한 경우다.</li>
</ul>
<h3 id="발음하기-쉬운-이름을-사용하라">발음하기 쉬운 이름을 사용하라</h3>
<ul>
<li>발음하기 어려운 이름은 토론하기도 어렵다. </li>
<li>genymdhms(generate date, year, month, day, hour, second)라는 단어 대신 generationTimestamp를 사용하면 지적인 대화가 가능해진다.</li>
</ul>
<h3 id="검색하기-쉬운-이름을-사용하라">검색하기 쉬운 이름을 사용하라</h3>
<ul>
<li>문자 하나를 사용하는 이름과 상수는 텍스트 코드에서 쉽게 눈에 띄지 않는다는 문제점이 있다. </li>
<li>긴 이름이 짧은 이름보다 좋다. </li>
<li>검색하기 쉬운 이름이 상수보다 좋다.</li>
<li>이름 길이는 범위 크기에 비례해야 한다.</li>
<li>변수나 상수를 코드 여러 곳에서 사용한다면 검색하기 쉬운 이름이 바람직하다.</li>
</ul>
<h3 id="인코딩을-피하라">인코딩을 피하라</h3>
<ul>
<li>유형이나 범위 정보까지 인코딩에 넣으면 그만큼 이름을 해독하기 어려워진다. </li>
<li>인코딩한 이름은 거의가 발음하기 어려우며 오타가 생기기도 쉽다.</li>
</ul>
<h3 id="자신의-기억력을-자랑하지-마라">자신의 기억력을 자랑하지 마라</h3>
<ul>
<li>코드를 읽으면서 변수 이름을 자신이 아는 이름으로 변환해야 한다면 그 변수 이름은 바람직하지 못하다.</li>
<li>문자 하나만 사용하는 변수 이름은 문제가 있다. 루프에서 반복 횟수를 세는 변수 i, j, k는 괜찮다.</li>
<li>똑똑한 프로그래머와 전문가 프로그래머 사이에서 나타나는 차이점 하나만 들자면, 전문가 프로그래머는 <strong>명료함</strong>이 최고라는 사실을 이해한다.</li>
<li>전문가 프로그래머는 자신의 능력을 좋은 방향으로 사용해 남들이 이해하는 코드를 내놓는다. </li>
</ul>
<h3 id="클래스-이름">클래스 이름</h3>
<ul>
<li>클래스 이름과 객체 이름은 명사나 명사구가 적합하다. </li>
</ul>
<h3 id="메서드-이름">메서드 이름</h3>
<ul>
<li>메서드 이름은 동사나 동사구가 적합하다. </li>
<li>접근자: get</li>
<li>변경자: set</li>
<li>조건자: is</li>
</ul>
<h3 id="기발한-이름은-피하라">기발한 이름은 피하라</h3>
<ul>
<li>특정 문화에서만 사용하는 농담은 피하는 편의 좋다. </li>
<li>의도를 분명하고 솔직하게 표현하라</li>
</ul>
<h3 id="한-개념에-한-단어를-사용하라">한 개념에 한 단어를 사용하라</h3>
<ul>
<li>추상적인 개념 하나에 단어 하나를 선택해 이를 고수한다.</li>
<li>똑같은 메서드를 클래스마다 fetch, retrieve, get으로 제각각 부르면 혼란스럽다. </li>
<li>일관성 있는 어휘는 코드를 사용할 프로그래머가 반갑게 여길 선물이다. </li>
</ul>
<h3 id="말장난을-하지-마라">말장난을 하지 마라</h3>
<ul>
<li>한 단어를 두 가지 목적으로 사용하지 마라. </li>
<li>여러 클래스에 add라는 메서드가 생겼다. 지금까지 구현한 add 메서드는 모두가 기존 값 두 개를 더하거나 이어서 새로운 값을 만든다고 가정하자. 새로 작성하는 메서드는 집합에 값 하나를 추가한다. 이 메서드를 add라 불러도 괜찮을까? 새 메서드는 기존 add 메서드와 맥락이 다르다. 그러므로 insert나 append라는 이름이 적당하다. </li>
<li>프로그래머는 코드를 최대한 이해하기 쉽게 짜야 한다. </li>
</ul>
<h3 id="해법-영역에서-가져온-이름을-사용하라">해법 영역에서 가져온 이름을 사용하라</h3>
<ul>
<li>전산 용어, 알고리즘 이론, 패턴 이름, 수학 용어 등을 사용해도 괜찮다.</li>
<li>모든 이름을 문제 영역에서 가져오는 정책은 현명하지 못하다. </li>
<li>기술 개념에는 기술 이름이 가장 적합한 선택이다.</li>
</ul>
<h3 id="문제-영역에서-가져온-이름을-사용하라">문제 영역에서 가져온 이름을 사용하라</h3>
<ul>
<li>문제 영역 개념과 관련이 깊은 코드라면 문제 영역에서 이름을 가져와야 한다. </li>
</ul>
<h3 id="의미-있는-맥락을-추가하라">의미 있는 맥락을 추가하라</h3>
<ul>
<li>firstName, lastName, street, houseNumber, city, state, zipcode라는 변수가 있다. 변수를 훑어보면 주소라는 사실을 금방 알아챈다. 하지만 어느 메서드가 state라는 변수 하나만 사용한다면?</li>
<li>addr라는 접두어를 추가해 addrFirstName, addrLastName, addrState라 쓰면 맥락이 좀 더 분명해진다. 물론 Address라는 클래스를 생성하면 더 좋다. </li>
<li>맥락을 개선하면 함수를 쪼개기가 쉬워지므로 알고리즘도 좀 더 명확해진다.</li>
</ul>
<h3 id="불필요한-맥락을-없애라">불필요한 맥락을 없애라</h3>
<ul>
<li>이름에 불필요한 맥락을 추가하지 않도록 주의한다.</li>
<li>accountAddress와 customerAddress는 Address 클래스 인스턴스로는 좋은 이름이나 클래스 이름으로는 적합하지 못하다. Address는 클래스 이름으로 적합하다.</li>
</ul>
<h3 id="마치면서">마치면서</h3>
<ul>
<li>좋은 이름을 선택하려면 설명 능력이 뛰어나야 하고 문화적인 배경이 같아야 한다.</li>
<li>다른 사람이 짠 코드를 손본다면 리팩터링 도구를 사용해 문제 해결 목적으로 이름을 개선하라.</li>
</ul>
<br>

<hr>
<p>📘 코딩을 할 때 쉬운 것 같으면서 어려운 일이 변수, 함수 네이밍이다. 이름만 잘 지어도 코드의 질은 훨씬 높아진다는 것을 오늘 내용을 읽으며 다시금 깨달았다. 반성하자면, 이름에 정보가 드러나지 않은 채 나만 아는 단어나 문자로 이름을 붙인 적이 많다. 이름을 잘 설정한다면 주석 처리를 하지 않고도 코드를 읽었을 때 이해가 잘될 것이다. 코드만 이해하기 쉽게 짜는 것이 아닌, 이름도 이해하기 쉽게 짜야 할 것이다. 오늘 내용을 바탕으로 네이밍에 조금 더 주의를 기울여보자! 
<br>
<br></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ [Clean Code]클린코드_추천사, 0.들어가면서, 1. 깨끗한 코드]]></title>
            <link>https://velog.io/@florence_y/Clean-Code%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C%EC%B6%94%EC%B2%9C%EC%82%AC-0.%EB%93%A4%EC%96%B4%EA%B0%80%EB%A9%B0</link>
            <guid>https://velog.io/@florence_y/Clean-Code%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C%EC%B6%94%EC%B2%9C%EC%82%AC-0.%EB%93%A4%EC%96%B4%EA%B0%80%EB%A9%B0</guid>
            <pubDate>Tue, 11 Jun 2024 11:01:20 GMT</pubDate>
            <description><![CDATA[<h1 id="✨-clean-code">✨ Clean Code</h1>
<blockquote>
<p>🧐 개발자라면 누구나 협업하기 좋은 코드, 유지보수를 효율적으로 할 수 있는 코드 등 좋은 코드에 대해 끊임없이 고민할 것이다. 나 또한 더 나은 코드에 대한 고민을 하지만 정작 깨끗한 코드가 무엇인지 모르고, 깨끗한 코드를 알아보지 못한다. 좋은 코드에 대한 힌트를 얻고자 &lt;클린 코드&gt;를 읽고 내용 정리를 해보려 한다!</p>
</blockquote>
<h2 id="📌-추천사">📌 추천사</h2>
<ul>
<li>사소한 곳에서 발휘하는 정직은 사소하지 않다.</li>
<li>책임 있는 전문가라면 프로젝트를 시작할 때 생각하고 계획할 시간을 확보해야 한다.</li>
<li>세세함에 주의를 기울이는 태도는 전문가에게 더더욱 필수적인 자질이 되었다.</li>
<li>스크럼과 애자일에 관심이 모여진 현재는 제품을 신속하게 시장에 출시하는 방법론을 강조한다. 하지만 대다수 활동은 제조가 아니라 유지보수다.</li>
</ul>
<h4 id="🔑-5s-철학">🔑 5S 철학</h4>
<p>Lean의 토대 
→ 린(Lean)은 불필요한 낭비를 최소화하고 소프트웨어의 가치를 최대화하는 방식.</p>
<p><strong>1. 정리, 조직, 정렬(Sort):</strong> 적절한 명명법 등과 같은 방법을 사용해 무엇이 어디에 있는지 알아야 함.
<strong>2. 정돈, 단정함, 체계화(Seiton):</strong> 코드는 누구나 예상하는 위치에 있어야 함.
<strong>3. 청소, 정리, 광내기(Seiso):</strong> 주석 혹은 주석으로 처리한 코드는 제거해야 함.
<strong>4. 청결, 표준화(Seiketsu):</strong> 일관적인 구현 스타일과 기법 필요.
<strong>5. 생활화, 규율(Shutsuke):</strong> 관례를 따르고, 자기 작품을 자주 돌아보고, 기꺼이 변경하는 규율.</p>
<ul>
<li>품질은 하늘에서 뚝 떨어진 위대한 방법론이 아니라 사심 없이 기울이는 무수한 관심에서 얻어진다.</li>
<li>소프트웨어 설계에서 재작업은 가치를 가져온다.</li>
<li><strong>코드에 정직하고, 코드의 상태에 관하여 동료들에게 정직하고, 무엇보다도, 자기 코드에 대해서 자신에게 정직하라.</strong></li>
</ul>
<br>

<h2 id="📌-0-들어가면서">📌 0. 들어가면서</h2>
<ul>
<li>장인정신<ul>
<li>장인에게 필요한 원칙, 패턴, 기법, 경험이라는 지식 습득<ul>
<li>열심히 일하고 연습해 지식을 몸과 마음으로 체득</li>
</ul>
</li>
</ul>
</li>
<li>깨끗한 코드를 작성하는 방법은 매우 어렵다. 고생을 해야 한다.</li>
</ul>
<br>

<h2 id="📌-1-깨끗한-코드">📌 1. 깨끗한 코드</h2>
<h4 id="코드가-존재하리라">코드가 존재하리라</h4>
<ul>
<li>앞으로 코드가 사라질 가망은 전혀 없다! → 코드는 요구사항을 상세히 표현하는 수단이니까.</li>
<li>기계가 실행할 정도로 상세하게 요구사항을 명시하는 작업 = 프로그래밍. 이렇게 명시한 결과가 바로 코드.</li>
<li><strong>코드 = 요구사항을 표현하는 언어</strong></li>
<li>요구사항에 더욱 가까운 언어를 만들 수도 있고, 요구사항에서 정형 구조를 뽑아내는 도구를 만들 수도 있다. 하지만 어느 순간에는 정밀한 표현이 필요하다.</li>
</ul>
<h4 id="나쁜-코드">나쁜 코드</h4>
<ul>
<li>우리 모두는 자신이 짠 쓰레기 코드를 쳐다보며 나중에 손보겠다고 생각한 경험이 있다. 우리 모두는 대충 짠 프로그램이 돌아간다는 사실에 안도감을 느끼며 그래도 안 돌아가는 프로그램보다 돌아가는 쓰레기가 좋다고 스스로를 위로한 경험이 있다. </li>
<li>다시 돌아와 나중에 정리하겠다고 다짐했었다. <strong>나중은 결코 오지 않는다.</strong></li>
</ul>
<h4 id="나쁜-코드로-치르는-대가">나쁜 코드로 치르는 대가</h4>
<ul>
<li>나쁜 코드는 개발 속도를 크게 떨어뜨린다. </li>
<li>나쁜 코드가 쌓일수록 팀 생산성은 떨어진다. 그러다가 마침내 0에 근접한다.</li>
<li>빨리 가는 유일한 방법은, 언제나 코드를 최대한 깨끗하게 유지하는 습관이다.</li>
<li>깨끗한 코드를 작성하려면 &#39;청결&#39;이라는 힘겹게 습득한 감각을 활용해 자잘한 기법들을 적용하는 절제와 규율이 필요하다. <strong>열쇠는 &#39;코드 감각&#39;이다.</strong></li>
<li>&#39;코드 감각&#39;이 있는 프로그래머는 나쁜 모듈을 보면 좋은 모듈로 개선할 방안을 떠올린다.</li>
</ul>
<h3 id="💡-깨끗한-코드란">💡 깨끗한 코드란?</h3>
<ul>
<li><p><strong>논리 간단히, 의존성 최대한 줄여야</strong> 유지보수 쉬워짐.
   나쁜 코드 ⇒ 깨진 창문 비유 
→ 창문이 깨진 건물은 누구도 상관하지 않는다는 인상을 풍긴다. 사람들도 관심을 끊는다. 창문이 더 깨져도 상관하지 않고, 마침내는 자발적으로 창문을 깬다. 창문이 깨지고 나면 쇠퇴하는 과정이 시작된다.</p>
</li>
<li><p>메모리 누수, 경쟁 상태, 일관성 없는 명명법 등 <strong>오류 처리 꼼꼼히</strong> 처리.</p>
</li>
<li><p><strong>깨끗한 코드는 한 가지를 제대로</strong> 한다. 나쁜 코드는 너무 많은 일을 하려 애쓰다가 의도가 뒤섞이고 목적이 흐려진다.</p>
</li>
<li><p><strong>단순하고 직접적</strong>, 설계자의 의도 숨기지 않음.
   좋은 소설과 마찬가지로 깨끗한 코드는 해결한 문제의 긴장을 명확히 드러내야 한다.</p>
</li>
<li><p>작성자가 아닌 사람도 읽기 쉽고 고치기 쉬움. </p>
</li>
<li><p>단위 테스트 케이스와 인수 테스트 케이스가 존재.</p>
</li>
<li><p><strong>인간이 읽기 좋은 코드</strong>를 작성해야 함.</p>
</li>
<li><p>언제나 누군가 <strong>주의 깊게 짰다</strong>는 느낌을 줌.
  → 누군가 시간을 들여 깔끔하고 단정하게 정리한 코드다. 세세한 사항까지 꼼꼼하게 신경 쓴 코드다.</p>
</li>
<li><p>모든 테스트 통과, 중복 없음, 시스템 내 모든 설계 아이디어를 표현, 클래스 / 메서드 / 함수 등을 최대한 줄임. </p>
</li>
<li><p><strong>중복 줄이기, 표현력 높이기, 초반부터 간단한 추상화 고려</strong>하기.</p>
</li>
<li><p>코드가 그 문제를 풀기 위한 언어처럼 보인다면 아름다운 코드.</p>
</li>
<li><p>코드를 읽는 시간 : 코드를 짜는 시간 = 10:1을 훌쩍 넘는다. 그러므로 읽기 쉬운 코드가 매우 중요하다.</p>
</li>
</ul>
<h4 id="보이스카우트-규칙">보이스카우트 규칙</h4>
<blockquote>
<p>캠프장은 처음 왔을 때보다 더 깨끗하게 해놓고 떠나라.</p>
</blockquote>
<ul>
<li>지속적인 개선이야말로 전문가 정신의 본질이다.</li>
</ul>
<h4 id="결론">결론</h4>
<ul>
<li>예술에 대한 책을 읽는다고 예술가가 된다는 보장은 없다. 이 책 역시 마찬가지다. 이 책을 읽는다고 뛰어난 프로그래머가 된다는 보장은 없다. 단지 <strong>뛰어난 프로그래머가 생각하는 방식</strong>과 그들이 사용하는 <strong>기술과 기교와 도구</strong>를 소개할 뿐이다.</li>
</ul>
<p><strong>&quot;연습해, 연습!&quot;</strong></p>
<br>

<hr>
<p>📘 책을 읽으며 메이크업을 했던 시절이 떠올랐다. 눈썹의 대칭, 속눈썹의 각도 등 아주 사소한 부분이 전체를 좌우한다. 메이크업도 시간 내에 완성하는 것이 매우 중요한데, 시간에만 집중하고 디테일에 주의를 기울이지 않으면 완성도가 떨어지고 결국 내 실력은 발전하지 않는다. 프로그래밍도 메이크업과 닮았다는 생각이 들면서, 일정에 맞추기에 급급해 코드에 집중하기보다는 기능이 돌아가는지에만 집중했던 내 모습이 떠올라 부끄러워졌다. 물론 현업에서는 기한을 맞추는 것이 비즈니스 측면에서 매우 중요하기 때문에 꼼꼼하게 코드의 품질에만 집중하는 것은 불가할 것이다. 하지만 깨끗한 코드를 위한 디테일한 관심과 노력이 곧 나와 팀원, 나아가 팀과 회사에 도움이 될 것은 분명할 것이다. 
&#39;장인 정신&#39;은 내가 무언가를 처음 시작할 때마다 스스로에게 세뇌하는 마인드이다. 어떠한 분야의 전문가란 머리로 하는 사람이 아닌 몸과 마음으로 하는 사람이라고 생각한다. 장인 정신을 가지고 무의식에 새길 만큼 많은 시간과 노력을 들일수록 전문가에 가까워질 것이다. 이 책을 통해 장인 정신을 가진 개발자가 되기 위한 마인드를 정립할 수 있을 것 같아 기대된다. 연습하고, 연습하자!</p>
<br>
<br>]]></description>
        </item>
        <item>
            <title><![CDATA[PlanetScale의 Hobby 플랜이 종료되었다.. Supabase로 갈아타기]]></title>
            <link>https://velog.io/@florence_y/PlanetScale%EC%9D%98-Hobby-%ED%94%8C%EB%9E%9C%EC%9D%B4-%EC%A2%85%EB%A3%8C%EB%90%98%EC%97%88%EB%8B%A4..-Supabase%EB%A1%9C-%EA%B0%88%EC%95%84%ED%83%80%EA%B8%B0</link>
            <guid>https://velog.io/@florence_y/PlanetScale%EC%9D%98-Hobby-%ED%94%8C%EB%9E%9C%EC%9D%B4-%EC%A2%85%EB%A3%8C%EB%90%98%EC%97%88%EB%8B%A4..-Supabase%EB%A1%9C-%EA%B0%88%EC%95%84%ED%83%80%EA%B8%B0</guid>
            <pubDate>Wed, 17 Apr 2024 14:50:41 GMT</pubDate>
            <description><![CDATA[<p>PlanetScale은 MySQL과 호환되는 Serverless Database 플랫폼이다. 나는 이전 개인 프로젝트를 진행할 때 Hobby 플랜(무료 버전)을 사용했다. 다른 프로젝트에 집중하느라 한동안 PlanetScale의 데이터베이스를 건드리지 않았더니 sleep 모드로 전환되었고, 다시 활성화하려 했더니..</p>
<blockquote>
<p><strong>Note</strong>
The Hobby plan was deprecated on April 8th, 2024. Any Hobby databases that have not been migrated by April 8th, 2024 have been slept. Read the Hobby plan deprecation FAQ documentation more information about next steps.
If you were previously on the Hobby plan and you need to migrate your data, but your database has been slept, you can wake your database one time to export your data. Follow the instructions in the Hobby plan deprecation FAQ to dump your data.</p>
</blockquote>
<p>4월 8일에 Hobby 플랜이 중지되었다는 공지를 보게 되었다....
데이터베이스 연결이 되지 않아 웹 사이트는 제대로 동작하지 않았고, 나는 이를 해결하기 위해 PlanetScale의 플랜을 업그레이드하거나 다른 해결책을 찾아야만 했다.</p>
<p>이리저리 찾아본 결과 <strong>Supabase</strong>를 알게 되었다. Supabase는 <code>PostgreSQL</code> 기반의 DB 기능을 제공해주는 백엔드 서비스 플랫폼이다. 무료로 사용할 수 있다는 점이 마음에 들었다. 무료로 제공해주는 규모는 작지만, 나의 프로젝트 또한 작은 규모이기 때문에 문제 될 점이 없다고 생각했다.</p>
<p>이제 PlanetScale의 데이터베이스를 Supabase로 옮겨야 하는데.. 나는 Prisma와 함께 사용하고 있어서 Prisma seeding으로 데이터베이스를 저장할까 고민했다. 하지만 유저 정보 같은 데이터들도 저장해야 했기 때문에 기존의 데이터베이스를 한꺼번에 옮기는 것이 좋을 것으로 생각했다. </p>
<p>PlanetScale → Supabase로 마이그레이션 하는 방법을 친절하게 설명해 주신 블로그 글을 발견했다.
<a href="https://velog.io/@wjdghks963/planetscale-superbase-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98">https://velog.io/@wjdghks963/planetscale-superbase-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98</a>
이 글을 토대로 데이터 옮기기에 성공했다.</p>
<h3 id="supabase-시작하기">Supabase 시작하기</h3>
<p><a href="https://supabase.com">https://supabase.com</a></p>
<p>Supabase에서 회원가입을 완료하고, Dashboard의 <strong>New project</strong>를 클릭해 프로젝트를 만든다. 
프로젝트명과 데이터베이스 비밀번호를 설정하면 되는데, 나는 Generate a password로 비밀번호를 설정했다.
<img src="https://velog.velcdn.com/images/florence_y/post/6a967fdf-342a-48e6-bd5e-a12e3f6c9940/image.jpg" alt=""></p>
<p>Database 페이지에서 connect 버튼을 클릭하면 <strong>URI</strong>를 확인할 수 있다.
중간의 [YOUR-PASSWOD] 란에 아까 설정한 비밀번호를 넣어 사용하면 된다. 
<img src="https://velog.velcdn.com/images/florence_y/post/b4bdc2ab-9e18-45e0-b203-1567a3134113/image.jpg" alt=""></p>
<h3 id="prisma-수정">Prisma 수정</h3>
<p>이제 Prisma를 재설정해보자. 위의 URI는 프로젝트의 .env 파일의 <code>DATABASE_URL</code>으로 저장하는데, <code>Can&#39;t reach database server..</code> 이라는 연결 에러가 날 수 있다고 한다. 
이를 방지하기 위해 <code>DATABASE_URL</code>에 넣은 URI를 복사해 <code>DIRECT_URL</code> 변수로 이름을 수정하고 <code>DATABASE_URL</code> 변수에는 포트 번호 6543으로 저장, URI 뒤에 <code>?pgbouncer=true</code> 쿼리스트링을 추가해준다.</p>
<p><img src="https://velog.velcdn.com/images/florence_y/post/06bfec1a-db4e-46ec-8c1b-67a4d6929abe/image.jpg" alt=""></p>
<p>schema.prisma 파일도 수정해야 한다. <code>mysql → postgresql</code>로 바꾸고, <code>directUrl = env(&quot;DIRECT_URL&quot;)</code>을 추가한다.
<img src="https://velog.velcdn.com/images/florence_y/post/cd1901ff-3b27-4ce1-89b2-9651ca97f875/image.jpg" alt=""></p>
<p><code>npx prisma db push</code>로 Supabase에 테이블을 생성해준다.</p>
<h3 id="데이터베이스-옮기기">데이터베이스 옮기기</h3>
<p><a href="https://dbeaver.io/download/">https://dbeaver.io/download/</a>
DBever을 사용하여 작업을 진행했다. 로컬로 MySQL을 켜고 DBever에서 데이터베이스 연결을 했다.</p>
<p><img src="https://velog.velcdn.com/images/florence_y/post/6b337fe1-1558-484e-bd5f-46508410549c/image.jpg" alt=""></p>
<p>새 데이터베이스 연결 버튼을 클릭해 PostgreSQL도 연결해준다.
<img src="https://velog.velcdn.com/images/florence_y/post/3e8ef2a0-c06d-4cfa-a50b-dfb917a33a4f/image.jpg" alt=""></p>
<p>Connection Settings 창이 뜨는데, Supabase의 데이터베이스 정보들을 넣어야 한다. 
Supabase 화면의 왼쪽 하단의 <strong>설정</strong> 버튼 - <strong>Database</strong>에 들어가면 Host, User 정보가 있을 것이다. 
<img src="https://velog.velcdn.com/images/florence_y/post/10552311-9a22-4e61-be2a-01ea18dfd706/image.jpg" alt=""></p>
<p>Host - Host, Username - User, Password - 데이터베이스 비밀번호를 넣어준다.
<img src="https://velog.velcdn.com/images/florence_y/post/987de0f1-65d6-4d97-b5d1-224c1123f457/image.jpg" alt=""></p>
<p>그럼 이렇게 연결되었을 것이다!
<img src="https://velog.velcdn.com/images/florence_y/post/4571c0f3-752c-4354-bb9f-51d469664aaa/image.jpg" alt=""></p>
<p>이동시킬 데이터가 있는 MySQL에서 데이터 내보내기를 한다. 데이터 내보내기 창의 다음 버튼을 클릭하면 Choose 버튼으로 내보낼 postgres를 선택할 수 있다. public 선택 후 계속 다음 버튼을 누르면 데이터가 내보내기가 완료된다.
<img src="https://velog.velcdn.com/images/florence_y/post/1a2590e9-f4a7-4f79-8326-63ff3aa4f8c0/image.jpg" alt="">
<img src="https://velog.velcdn.com/images/florence_y/post/4da3e382-a0d8-4e1a-bf67-740fa51317dd/image.jpg" alt=""></p>
<h3 id="프로젝트-재배포하기">프로젝트 재배포하기</h3>
<p>수정된 <code>DATABASE_URL</code>, <code>DIRECT_URL</code>를 환경변수에 저장하고 재배포하면 프로젝트가 정상적으로 작동할 것이다! 👏👏👏</p>
<br>
<br>
<br>
참고

<p><a href="https://velog.io/@wjdghks963/planetscale-superbase-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98">https://velog.io/@wjdghks963/planetscale-superbase-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98</a></p>
<p><a href="https://www.heropy.dev/p/bCffI2">https://www.heropy.dev/p/bCffI2</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL]Prisma_Seeding]]></title>
            <link>https://velog.io/@florence_y/TILPrismaSeeding</link>
            <guid>https://velog.io/@florence_y/TILPrismaSeeding</guid>
            <pubDate>Wed, 03 Jan 2024 15:22:12 GMT</pubDate>
            <description><![CDATA[<h1 id="prisma-seeding">Prisma Seeding</h1>
<p>Database Seeding은 초기 데이터 셋으로 데이터베이스를 채우는 것을 의미한다. Prisma에서 Database Seed하는 방법을 알아보려 한다.</p>
<h3 id="seedjs-파일-생성">seed.js 파일 생성</h3>
<p><code>/prisma</code> 폴더 안에 <code>seed.js</code> 파일을 생성한다.</p>
<h3 id="seedjs-파일-작성">seed.js 파일 작성</h3>
<p><code>prisma.schema</code>에서 User, Post 모델을 정의했다고 가정해보자. User에 새로운 Post를 추가하는 스크립트를 작성해보자.</p>
<pre><code class="language-js">import { PrismaClient } from &quot;@prisma/client&quot;;

const prisma = new PrismaClient();

// name이 Kim인 데이터가 없다면 생성
async function main() {
  const data = await prisma.user.upsert({
    where: { name: &#39;Kim&#39; },
    update: {},
    create: {
      name: &#39;Kim&#39;,
      posts: {
        create: {
          title: &#39;new title&#39;,
          content: &#39;new content&#39;,
        },
      },
    },
  })
  console.log({ data });
}

main()
  .then(async () =&gt; {
    await prisma.$disconnect()
  })
  .catch(async (e) =&gt; {
    console.error(e)
    await prisma.$disconnect()
    process.exit(1)
  })</code></pre>
<p>배열 형식의 데이터로 seed를 진행할 수도 있다.</p>
<pre><code class="language-js">// /util/data.js

const users = [
  { name: &#39;User1&#39; }, 
  { name: &#39;User2&#39; }, 
  { name: &#39;User3&#39; },
  { name: &#39;User4&#39; }
  { name: &#39;User5&#39; }
  { name: &#39;User6&#39; }
  { name: &#39;User7&#39; }
  { name: &#39;User8&#39; }
...
]

export default users;</code></pre>
<pre><code class="language-js">// /prisma/seed.js
import { PrismaClient } from &quot;@prisma/client&quot;;
import users from &quot;../util/data.js&quot;

const prisma = new PrismaClient();

async function main() {
  for(let user of users) {
    const userData = await prisma.user.create({
      data: user,
  });
  console.log({ data });
}

main()
  .then(async () =&gt; {
    await prisma.$disconnect()
  })
  .catch(async (e) =&gt; {
    console.error(e)
    await prisma.$disconnect()
    process.exit(1)
  })</code></pre>
<h3 id="packagejson-파일-수정">package.json 파일 수정</h3>
<p><code>package.json</code> 파일의 <code>&quot;prisma&quot;</code> 키에 <code>&quot;seed&quot;</code> 키를 추가한다.</p>
<pre><code class="language-json">// package.json
...
&quot;prisma&quot;: {
  &quot;seed&quot;: &quot;node prisma/seed.js&quot;
},</code></pre>
<h3 id="seeding">Seeding</h3>
<p>터미널에 아래 명령어를 입력하여 seed를 진행한다.</p>
<pre><code>npx prisma db seed</code></pre><h2 id="💥참고-nextjs_import-오류">💥참고) Next.js_import 오류</h2>
<p>seed를 진행하는 명령어를 입력했지만, 아래와 같은 에러가 발생했다.
<code>SyntaxError: Cannot use import statement outside a module</code></p>
<p>에러를 해결하기 전에 <strong>CommnJS</strong>와 <strong>ES modules</strong>에 대해 간단히 짚어보자. </p>
<blockquote>
<p> <strong>CommonJS</strong> : <code>require()</code>, <code>module.exports</code> 키워드를 사용하여 모듈에 접근
 <strong>ES modules</strong> : <code>import</code>, <code>export</code> 키워드를 사용하여 모듈에 접근</p>
</blockquote>
<h4 id="발생-원인">발생 원인</h4>
<p>Node.js에서는 기본적으로 <strong>CommonJS</strong>를 제공해왔다. <code>package.json</code> 파일에 <code>&quot;type&quot;</code> 필드가 없거나 <code>&quot;type&quot;: &quot;commonjs&quot;</code> 가 포함되었다면, JS 파일은 <strong>CommonJS</strong>로 처리된다. <code>import</code> 키워드를 사용하기 위해서는 <strong>ES modules</strong>로 처리한다는 것을 명시해야 하는데, 명시하지 않았기 때문에 에러가 발생한 것이다.</p>
<p>📍 하지만 Next.js 12부터 <strong>ES modules</strong>를 지원한다는데 왜 문제가 발생한 것일까..?
<code>next.config.js</code> 파일은 <strong>ES modules</strong>를 지원하지 않는다고 한다. 또한 node를 사용하여 <code>npm</code> 스크립트를 실행하면 이와 같은 문제가 발생할 수 있다고 한다.</p>
<h4 id="해결-방법">해결 방법</h4>
<ol>
<li><p><code>package.json</code> 파일에 <code>&quot;type&quot;: &quot;module&quot;</code>를 추가해주거나</p>
<pre><code class="language-json">{
 &quot;name&quot;: &quot;next_prisma&quot;,
 &quot;version&quot;: &quot;0.1.0&quot;,
 &quot;type&quot;: &quot;module&quot;,
...
}</code></pre>
</li>
<li><p><code>next.config.js</code> → <code>next.config.mjs</code>로 확장자를 변경한 후 파일 안의 <code>module.exports</code>를 <code>export default</code>로 바꿔준다.</p>
</li>
</ol>
<pre><code class="language-js">// next.config.mjs

/** @type {import(&#39;next&#39;).NextConfig} */
const nextConfig = {};

export default nextConfig;</code></pre>
<br>
<br>
<br>


<p>참고</p>
<p><a href="https://www.prisma.io/docs/orm/prisma-migrate/workflows/seeding">https://www.prisma.io/docs/orm/prisma-migrate/workflows/seeding</a></p>
<p><a href="https://www.codeconcisely.com/posts/nextjs-esm/">https://www.codeconcisely.com/posts/nextjs-esm/</a></p>
<p><a href="https://nodejs.org/api/packages.html#type">https://nodejs.org/api/packages.html#type</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL]Prisma_Prisma Client]]></title>
            <link>https://velog.io/@florence_y/TILPrisma-Client</link>
            <guid>https://velog.io/@florence_y/TILPrisma-Client</guid>
            <pubDate>Wed, 03 Jan 2024 09:20:27 GMT</pubDate>
            <description><![CDATA[<p>🚀지난번 Prisma를 설치하고 데이터베이스에 연결하는 것을 알아보았다. 오늘은 데이터베이스에 어떻게 접근하고 조작하는지 알아보려 한다.</p>
<h1 id="prisma-client">Prisma Client</h1>
<p><strong>Prisma Client</strong>란 데이터베이스 스키마에 맞게 자동으로 생성되는 데이터베이스 클라이언트이다. 쉽게 말해 Prisma를 통해 생성한 <strong>데이터베이스 테이블에 쉽게 접근할 수 있게 해주는 도구</strong>라고 할 수 있다. </p>
<h3 id="prisma-client-설치">Prisma Client 설치</h3>
<p>아래 명령어로 PrismaClient를 설치한다.</p>
<pre><code>npm install @prisma/client</code></pre><p>설치 후 아래 명령어로 Prisma Client를 생성해준다.</p>
<pre><code>prisma generate</code></pre><p>📢 Prisma Schema에 변경 사항이 있을 때마다 <code>npx prisma generate</code>를 실행해 Prisma Client를 업데이트해줘야 한다.</p>
<p>이제 데이터베이스 접근이 필요한 곳에서 PrismaClient를 인스턴스화 하여 사용하면 된다.</p>
<pre><code class="language-tsx">import { PrismaClient } from &#39;@prisma/client&#39;;

const prisma = new PrismaClient();</code></pre>
<p>or</p>
<pre><code class="language-jsx">const { PrismaClient } = require(&#39;@prisma/client&#39;);

const prisma = new PrismaClient();</code></pre>
<h3 id="💡-nextjs에서의-prisma-client">💡 Next.js에서의 Prisma Client</h3>
<p>Next.js의 경우 개발 환경에서 핫 리로딩(코드 변경시 변경된 코드 재실행. 파일을 저장할 때마다 자바스크립트 파일들이 재실행됨.)으로 인해 매번 새로운 PrismaClient 인스턴스가 초기화되고 데이터베이스에 대한 연결이 생성된다. 이에 따라 너무 많은 데이터베이스 연결이 생기면 데이터베이스 입출력이 매우 느려지거나 오류가 발생할 수 있다. 이를 방지하기 위해 PrismaClient를 인스턴스화하여 gloabalThis 전역 객체에 저장해서 사용한다.</p>
<pre><code class="language-jsx">// lib/prisma.js

const { PrismaClient } = require(&quot;@prisma/client&quot;);

// PrismaClient가 global에 없는 경우에만 인스턴스화
const prisma = globalThis.prisma || new PrismaClient();

// 프로덕션 상태가 아닐 때 globalThis 전역 객체에 저장
if (process.env.NODE_ENV !== &quot;production&quot;) globalThis.prisma = prisma;

export default prisma;</code></pre>
<h3 id="prisma-client-사용">Prisma Client 사용</h3>
<p>데이터 접근이 필요한 곳에서 인스턴스화한 PrismaClient를 불러와 사용한다. Prisma Client API로 CRUD 작업을 수행할 수 있다.</p>
<h4 id="create">Create</h4>
<p><code>create</code> 새로운 데이터 생성</p>
<pre><code class="language-js">const member = await prisma.member.create({
  data: {
    name: &#39;Kim&#39;,
    age: 20
  },
})</code></pre>
<p><code>createMany</code> 여러 데이터 생성</p>
<pre><code class="language-js">const createMembers = await prisma.member.createMany({
  data: [
    { name: &#39;Kim&#39;, age: 22 },
    { name: &#39;Lee&#39;, age: 23 },
    { name: &#39;Park&#39;, age: 23 },
  ],
})</code></pre>
<h4 id="read">Read</h4>
<p><code>findUnique</code> 특정 데이터 가져오기</p>
<pre><code class="language-js">// name이 Kim인 데이터 조회
const member = await prisma.member.findUnique({
  where: {
    name: &#39;Kim&#39;,
  },
})</code></pre>
<p><code>findMany</code> 여러 데이터 가져오기</p>
<pre><code class="language-js">// 모든 데이터 가져오기
const members = await prisma.member.findMany()</code></pre>
<pre><code class="language-js">// 조건에 맞는 여러 데이터 가져오기
// age가 23인 모든 데이터 조회
const members = await prisma.member.findMany({
  where: {
    age: 23,
  },
})</code></pre>
<h4 id="update">Update</h4>
<p><code>update</code> where 안의 속성을 가진 데이터 업데이트</p>
<pre><code class="language-js">// name이 Park인 데이터의 age를 24로 업데이트
const updateMember = await prisma.member.update({
  where: {
    name: &#39;Park&#39;,
  },
  data: {
    age: 24,
  },
})</code></pre>
<p><code>updateMany</code> where 안의 속성을 가진 모든 데이터 업데이트</p>
<pre><code class="language-js">// age가 23 이상인 모든 데이터의 role을 ADMIN으로 업데이트
const updateMembers = await prisma.member.updateMany({
  where: {
    age: {
      gte: 23,
    },
  },
  data: {
    role: &#39;ADMIN&#39;,
  },
})</code></pre>
<p><code>upsert</code> where 안의 속성을 가진 데이터가 있다면 업데이트, 없다면 데이터 생성</p>
<pre><code class="language-js">// name이 Jo인 데이터가 있다면 age를 20으로 업데이트, 없다면 name이 Jo이고 age가 20데이터 생성
const upsertMember = await prisma.member.upsert({
  where: {
    name: &#39;Jo&#39;,
  },
  update: {
    age: 20,
  },
  create: {
    name: &#39;Jo&#39;,
    age: 20,
  },
})</code></pre>
<h4 id="delete">Delete</h4>
<p><code>delete</code> where 안의 속성을 가진 데이터 삭제</p>
<pre><code class="language-js">// age가 22인 데이터 삭제하기
const deleteMember = await prisma.member.delete({
  where: {
    age: 22,
  },
})</code></pre>
<p><code>deleteMany</code> 여러 데이터 삭제</p>
<pre><code class="language-js">// 모든 데이터 삭제하기
const members = await prisma.member.deleteMany()</code></pre>
<pre><code class="language-js">// 조건에 맞는 여러 데이터 삭제하기
// age가 23인 모든 데이터 삭제
const members = await prisma.member.deleteMany({
  where: {
    age: 23,
  },
})</code></pre>
<h2 id="정리">정리</h2>
<ul>
<li>Prisma Client: 데이터베이스 테이블에 쉽게 접근할 수 있게 해주는 도구</li>
<li>Prisma Schema에 변경 사항이 있을 때마다 <code>npx prisma generate</code>를 실행하여 Prisma Client를 업데이트해야 한다.</li>
<li>Next.js에서는 따로 prisma.js 파일을 만들어 Prisma Client를 인스턴스화하여 사용한다.</li>
</ul>
<br>
<br>
<br>
참고

<p><a href="https://www.prisma.io/docs/orm/prisma-client/setup-and-configuration/generating-prisma-client#why-is-prisma-client-generated-into-node_modulesprismaclient-by-default">https://www.prisma.io/docs/orm/prisma-client/setup-and-configuration/generating-prisma-client#why-is-prisma-client-generated-into-node_modulesprismaclient-by-default</a></p>
<p><a href="https://www.prisma.io/docs/orm/more/help-and-troubleshooting/help-articles/nextjs-prisma-client-dev-practices">https://www.prisma.io/docs/orm/more/help-and-troubleshooting/help-articles/nextjs-prisma-client-dev-practices</a></p>
<p><a href="https://medium.com/@2018.itsuki/postgresql-with-next-js-and-prisma-44f66a05378a">https://medium.com/@2018.itsuki/postgresql-with-next-js-and-prisma-44f66a05378a</a></p>
<p><a href="https://www.prisma.io/docs/orm/prisma-client/queries/crud">https://www.prisma.io/docs/orm/prisma-client/queries/crud</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL]Prisma+MySQL 연결하기]]></title>
            <link>https://velog.io/@florence_y/TILPrismaMySQL-%EC%97%B0%EA%B2%B0%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@florence_y/TILPrismaMySQL-%EC%97%B0%EA%B2%B0%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 02 Jan 2024 14:43:16 GMT</pubDate>
            <description><![CDATA[<h1 id="prisma">Prisma</h1>
<p>Prisma는 직관적인 데이터 모델, 자동 마이그레이션, 타입 안전성 및 자동 완성 기능을 제공하는 차세대 ORM이다.
📌 ORM: Object-Relational Mapping. 객체로 연결해준다는 의미. 어플리케이션과 데이터베이스 연결 시 SQL 언어가 아닌 어플리케이션 개발 언어를 사용하여 데이터베이스에 접근할 수 있게 해주는 툴</p>
<h3 id="prisma-설치">Prisma 설치</h3>
<p>아래 명령어로 Prisma를 설치한다.</p>
<pre><code>npm install prisma --save-dev</code></pre><p>설치 후 아래 명령어로 Prisma를 실행한다.</p>
<pre><code>npx prisma init</code></pre><p>실행하면 프로젝트 파일의 루트 디렉토리에 <code>prisma</code> 폴더와 <code>.env</code> 파일이 생성된다.</p>
<h3 id="데이터베이스-연결">데이터베이스 연결</h3>
<p>MySQL에 접속해 아래 명령어로 연결할 데이터베이스를 생성한다.</p>
<pre><code class="language-sql">CREATE DATABASE my_db;</code></pre>
<p>.env 파일에는 데이터베이스 URL을 저장할 수 있다.</p>
<pre><code class="language-shell">DATABASE_URL=&quot;db종류://유저이름:비밀번호@HOST:포트/데이터베이스&quot;</code></pre>
<ul>
<li>예시<pre><code class="language-shell">DATABASE_URL=&quot;mysql://root:1234@localhost:3306/my_db&quot;</code></pre>
</li>
</ul>
<h3 id="schema-파일-작성">schema 파일 작성</h3>
<p><code>prisma</code> 폴더에 들어가면 <code>schema.prisma</code> 파일이 존재한다. <code>schema.prisma</code> 파일은 Prisma를 설정하는 메인 파일이다. </p>
<p><code>schema.prisma</code> 파일은 크게 세 부분으로 나뉜다.</p>
<ul>
<li><strong>Generators</strong> Prisma client를 기반으로 생성할 클라이언트를 지정하는 부분</li>
<li><strong>Data sources</strong>  어떤 데이터베이스와 연결할지 설정하는 부분</li>
<li><strong>Data model definition</strong> 데이터 모델 및 관계 정의하는 부분</li>
</ul>
<p>아래와 같은 양식으로 작성한다.</p>
<pre><code class="language-js">// prisma/schema.prisma

generator client {
    provider = &quot;prisma-client-js&quot;
}

datasource db {
    provider = &quot;mysql&quot;
    url      = env(&quot;DATABASE_URL&quot;)
}

model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  name      String
  password  String
  posts     Post[]
}

model Post {
  id        Int      @id @default(autoincrement())
  createdAt DateTime @default(now())
  title     String?
  content   String?
  author    User     @relation(fields: [authorId], references: [id])
  authorId  Int
}

...</code></pre>
<p>각 모델은 하나의 id만 가질 수 있고 <code>@default()</code>로 기본값을 설정, <code>autoincrement()</code>로 자동 증가하는 id값을 설정할 수 있다.</p>
<p><code>String?</code> 은 String 혹은 null이 될 수 있다는 의미이다.</p>
<p>User와 Post 간 1:N 관계가 있을 때 <code>Post[]</code>로 표현, Post 모델에서는 <code>@relation(fields: [authorId], references: [id])</code>를 입력한다.
→ Post의 <code>authorId</code>는 User의 <code>id</code>를 참조한다는 의미.</p>
<h3 id="prisma-migrate">Prisma Migrate</h3>
<p>마이그레이션을 통해 <code>schema.prisma</code>에서 정의한 Schema를 바탕으로 DB에 테이블을 생성할 수 있다. </p>
<pre><code>npx prisma migrate dev --name init</code></pre><p>위의 명령어로 마이그레이션을 실행하면 <code>prisma/migrations/날짜_init/migration.sql</code> 파일이 생성된다.</p>
<p>마이그레이션 실행 후, MySQL에 접속해 생성된 테이블을 확인할 수 있다.</p>
<pre><code>mysql&gt; SHOW TABLES;
+--------------------+
| Tables_in_my_db    |
+--------------------+
| _prisma_migrations |
| user               |
| post               |
+--------------------+</code></pre><br>
<br>
<br>


<p>참조</p>
<p><a href="https://www.prisma.io/docs/orm/overview/introduction/what-is-prisma">https://www.prisma.io/docs/orm/overview/introduction/what-is-prisma</a></p>
<p><a href="https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases-node-mysql">https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases-node-mysql</a></p>
<p><a href="https://jalynne-kim.medium.com/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EB%B0%B1%EC%97%94%EB%93%9C-orm-object-relational-mapping-%EC%9D%98-%EA%B0%9C%EB%85%90%EA%B3%BC-%EC%A2%85%EB%A5%98-%ED%99%9C%EC%9A%A9%EB%B0%A9%EC%95%88-c43b69028957">https://jalynne-kim.medium.com/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EB%B0%B1%EC%97%94%EB%93%9C-orm-object-relational-mapping-%EC%9D%98-%EA%B0%9C%EB%85%90%EA%B3%BC-%EC%A2%85%EB%A5%98-%ED%99%9C%EC%9A%A9%EB%B0%A9%EC%95%88-c43b69028957</a></p>
<p><a href="https://www.prisma.io/docs/orm/prisma-schema/data-model/relations">https://www.prisma.io/docs/orm/prisma-schema/data-model/relations</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL]JavaScript_Map, Set]]></title>
            <link>https://velog.io/@florence_y/TILJavaScriptSet-Map</link>
            <guid>https://velog.io/@florence_y/TILJavaScriptSet-Map</guid>
            <pubDate>Tue, 17 Oct 2023 14:54:58 GMT</pubDate>
            <description><![CDATA[<h2 id="map">Map</h2>
<p><code>Map</code>은 키가 있는 값이 저장된 컬렉션이다.</p>
<h3 id="맵과-객체의-차이">맵과 객체의 차이</h3>
<ul>
<li>객체: 키를 문자형으로 반환</li>
<li>맵: 타입 변환 없이 그대로 유지</li>
</ul>
<h3 id="map의-주요-메서드">Map의 주요 메서드</h3>
<ul>
<li><code>new Map()</code>  맵 생성</li>
<li><code>map.set(key, value)</code> key를 이용해 value를 저장</li>
<li><code>map.get(key)</code> key에 해당하는 값 반환 (key가 존재하지 않으면 undefined 반환)</li>
<li><code>map.has(key)</code> key가 존재하면 true, 존재하지 않으면 false 반환</li>
<li><code>map.delete(key)</code> key에 해당하는 값 삭제</li>
<li><code>map.clear()</code> 맵 안의 모든 요소 제거</li>
<li><code>map.size</code> 요소의 개수 반환</li>
</ul>
<blockquote>
<p>✔️ <code>map[key]</code> 를 사용할 수 있지만 <code>map</code>을 <strong>일반 객체처럼</strong> 취급하게 된다. <code>map</code>을 사용할 때는 <code>map</code>의 전용 메서드 <strong><code>set</code>, <code>get</code> 등을 사용</strong>하자.</p>
</blockquote>
<h3 id="map의-특징">Map의 특징</h3>
<p><strong>Map의 key에는 자료형 제약이 없다. map은 객체를 key로 사용할 수 있다.</strong></p>
<pre><code class="language-js">let john = { name: &quot;John&quot; };

let visitsCountMap = new Map();

visitsCountMap.set(john, 123);

console.log( visitsCountMap.get(john) ); // 123</code></pre>
<p><strong>맵 체이닝</strong></p>
<p><code>map.set(key, value)</code> 는 값을 설장할 뿐 아니라 자기 자신(map 객체)를 반환한다. 따라서 <code>map.set</code>을 &#39;체이닝(chaining)&#39;할 수 있다.</p>
<pre><code class="language-js">const map = new Map();

map
  .set(&#39;1&#39;, &#39;str1&#39;)
  .set(1, &#39;num1&#39;)
  .set(true, &#39;bool1&#39;);

console.log(map);
// Map(3) {&#39;1&#39; =&gt; &#39;str1&#39;, 1 =&gt; &#39;num1&#39;, true =&gt; &#39;bool1&#39;}</code></pre>
<h3 id="map의-요소-반복">Map의 요소 반복</h3>
<ul>
<li><code>map.keys()</code> – 각 요소의 키를 모은 반복 가능한(iterable, 이터러블) 객체를 반환</li>
<li><code>map.values()</code> – 각 요소의 값을 모은 이터러블 객체를 반환</li>
<li><code>map.entries()</code> – 요소의 <code>[키, 값]</code>을 한 쌍으로 하는 이터러블 객체를 반환</li>
</ul>
<pre><code class="language-js">let recipeMap = new Map([
  [&#39;cucumber&#39;, 500],
  [&#39;tomatoes&#39;, 350],
  [&#39;onion&#39;,    50]
]);

// key 대상으로 순회
for (let vegetable of recipeMap.keys()) {
  console.log(vegetable); // cucumber, tomatoes, onion
}

// value 대상으로 순회
for (let amount of recipeMap.values()) {
  console.log(amount); // 500, 350, 50
}

// [key, value] 대상으로 순회
for (let entry of recipeMap) {
  console.log(entry); // cucumber,500 ...
}


//내장 메서드 forEach 지원
recipeMap.forEach( (value, key, map) =&gt; {
  console.log(`${key}: ${value}`); // cucumber: 500 ...
});</code></pre>
<p>→ 맵은 값이 삽입된 순서대로 순회가 실시된다.</p>
<h3 id="objectentries-객체-→-맵">Object.entries: 객체 → 맵</h3>
<ol>
<li><code>Object.entries(obj)</code>는 객체를 [key, value] 쌍의 배열로 반환해준다.</li>
<li><code>new Map([...])</code>은 그 배열을 받아서 Map 객체를 만들어준다.<pre><code class="language-js">let obj = {
name: &quot;John&quot;,
age: 30
};
</code></pre>
</li>
</ol>
<p>let map = new Map(Object.entries(obj)); // [ [&quot;name&quot;,&quot;John&quot;], [&quot;age&quot;, 30] ]</p>
<p>console.log( map.get(&#39;name&#39;) ); // John</p>
<pre><code>
### Object.fromEntries: 맵 → 객체
`Object.fromEntries()`는 키-값 쌍의 이터러블(iterable)을 받아서 일반 객체로 변환해준다.
```js
let prices = Object.fromEntries([
  [&#39;banana&#39;, 1],
  [&#39;orange&#39;, 2],
  [&#39;meat&#39;, 4]
]);

// now prices = { banana: 1, orange: 2, meat: 4 }

console.log(prices.orange); // 2</code></pre><p>→ 주로 API나 다른 코드에서 Map으로 데이터를 관리하다가, 최종적으로 JSON으로 변환하거나 일반 객체로 사용하고 싶을 때 사용한다.</p>
<pre><code class="language-js">const map = new Map();
map.set(&#39;id&#39;, 1);
map.set(&#39;title&#39;, &#39;Hello&#39;);

const obj = Object.fromEntries(map);

// 이 obj는 이제 JSON.stringify(obj)처럼 쉽게 직렬화 가능</code></pre>
<h2 id="set">Set</h2>
<p><code>Set</code>은 중복을 허용하지 않는 값을 모아놓은 특별한 컬렉션이다. 키가 없는 값이 저장된다.</p>
<pre><code class="language-js">let set = new Set();

let john = { name: &quot;John&quot; };
let pete = { name: &quot;Pete&quot; };
let mary = { name: &quot;Mary&quot; };

// john, mary 여러 번 추가
set.add(john);
set.add(pete);
set.add(mary);
set.add(john);
set.add(mary);

// 중복 x
console.log( set.size ); // 3

for (let user of set) {
  console.log(user.name); // // John, Pete, Mary
}</code></pre>
<h3 id="set의-주요-메서드">Set의 주요 메서드</h3>
<ul>
<li><code>new Set(iterable)</code> – 셋 생성</li>
<li><code>set.add(value)</code> – 값 추가, 셋 자신 반환</li>
<li><code>set.delete(value)</code> – 값 제거. 호출 시점에 셋 내에 값이 있어서 제거에 성공하면 true, 아니면 false 반환</li>
<li><code>set.has(value)</code> – 셋 내에 값이 존재하면 true, 아니면 false 반환</li>
<li><code>set.clear()</code> – 셋 안의 모든 요소 제거</li>
<li><code>set.size</code> – 요소의 개수 반환</li>
</ul>
<h3 id="set의-값-반복">Set의 값 반복</h3>
<ul>
<li><code>for...of</code> 문<pre><code class="language-js">const mySet = new Set([1, 2, 3]);
</code></pre>
</li>
</ul>
<p>for (const value of mySet) {
  console.log(value); // 1, 2, 3
}</p>
<pre><code>
- `forEach()` 메서드
```js
set.forEach((value, valueAgain, set) =&gt; {
   console.log(`value: ${value}, valueAgain: ${valueAgain}`);
   // value: a, valueAgain: a
   // value: b, valueAgain: b
   // value: c, valueAgain: c

  // value, valueAgain은 같음
});</code></pre><p>→ Map객체의 <code>forEach()</code>는 <code>(value, key, map)</code> 형태이다. 하지만 Set은 key 없이 value만 존재하기 때문에 Set의 <code>forEach(callbackFn)</code>은 내부적으로 <code>callbackFn(value, value, set)</code> 형태로 호출된다.</p>
<ul>
<li><code>set.keys()</code> – 셋 내의 모든 값 포함하는 이터러블 객체 반환</li>
<li><code>set.values()</code> – <code>set.keys</code>와 동일한 작업. <code>맵</code>과의 호환성을 위해 만들어진 메서드</li>
<li><code>set.entries()</code> – 셋 내의 각 값을 이용해 만든 <code>[value, value]</code> 배열을 포함하는 이터러블 객체 반환. <code>맵</code>과의 호환성을 위해 만들어졌다.</li>
</ul>
<h2 id="정리">정리</h2>
<ul>
<li><code>Map</code>은 키가 있는 값이 저장된 컬렉션이다.</li>
<li><code>Map</code>의 key에는 자료형 제약이 없다.</li>
<li><code>map.set(key, value)</code> key를 이용해 value를 저장</li>
<li><code>map.get(key)</code> key에 해당하는 값 반환 (key가 존재하지 않으면 undefined 반환)</li>
<li><code>map.delete(key)</code> key에 해당하는 값 삭제</li>
<li><code>map.size</code> 요소의 개수 반환</li>
<li><code>Set</code>은 중복을 허용하지 않는 값을 모아놓은 특별한 컬렉션이다.</li>
<li><code>set.add(value)</code> – 값 추가, 셋 자신 반환</li>
<li><code>set.delete(value)</code> – 값 제거</li>
<li><code>set.size</code> – 요소의 개수 반환</li>
</ul>
<br>
<br>
<br>
참고

<p>모던 자바스크립트 Deep Dive (도서)
<a href="https://ko.javascript.info/map-set">https://ko.javascript.info/map-set</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL]JavaScript_클래스]]></title>
            <link>https://velog.io/@florence_y/TIL-p9rjr8hd</link>
            <guid>https://velog.io/@florence_y/TIL-p9rjr8hd</guid>
            <pubDate>Fri, 16 Jun 2023 14:23:01 GMT</pubDate>
            <description><![CDATA[<h2 id="클래스classes">클래스(Classes)</h2>
<p>C++이나 Java 같은 클래스 기반 객체지향 프로그래밍 언어는 클래스를 통해 상속한다. 하지만 자바스크립트는 클래스가 없고, 프로토타입 기반으로 객체 복제를 통해 객체를 생성한다.</p>
<p>ES6에서 자바스크립트의 클래스가 도입되었는데, 클래스 기반 객체지향 프로그래밍 언어와 매우 비슷한 문법 구조를 가지고 있다. 하지만 클래스는 함수이며 기존 프로토타입 기반 패턴을 클래스 기반 패턴처럼 사용할 수 있는 문법적 설탕이라고도 한다.</p>
<p>클래스를 통해 여러 객체를 만들 수 있는데, 쉽게 말해 <strong>클래스 = 붕어빵 틀</strong>, <strong>객체 = 붕어빵</strong>이라고 할 수 있다.</p>
<h3 id="생성자-함수와-클래스-문법의-차이">생성자 함수와 클래스 문법의 차이</h3>
<pre><code class="language-js">// 생성자 함수
function User({name, age}) {
  this.name = name;
  this.age = age;
}
// prototype에 메서드 추가
User.prototype.sayHello = function() {
  return `Hello, ${this.name}!`;
};

const user = new User({name: &#39;Lee&#39;, age: 13});
console.log(user.sayHello());

// Hello, Lee!</code></pre>
<p>클래스는 <code>class</code> 키워드를 사용해 생성하고, <code>new</code> 연산자와 함께 호출하면 내부에서 정의한 메서드가 들어있는 객체가 생성된다.</p>
<pre><code class="language-js">// class 문법
class User {
  // constructor 키워드로 생성자 정의
  constructor({name, age}) {
    this.name = name;
    this.age = age;
  }
  // prototype에 메서드 추가
  sayHello() {
    return `Hello, ${this.name}!`;
  }
}

const user = new User({name: &#39;Lee&#39;, age: 13});
console.log(user.sayHello());

// Hello, Lee!</code></pre>
<h3 id="클래스-정의">클래스 정의</h3>
<h4 id="클래스-선언">클래스 선언</h4>
<p>클래스를 선언하기 위해 <code>class</code> 키워드와 클래스 이름(예시에서 User)을 함께 사용한다. 클래스 내부의 <code>constructor</code> 메서드는 클래스 안에 한 개만 존재할 수 있다. <code>constructor</code> 내부에서 인스턴스의 생성 및 초기화가 이루어진다.</p>
<pre><code class="language-js">class User{
  constructor({name, age}) {
    this.name = name;
    this.age = age;
  }
}</code></pre>
<h4 id="메서드-정의">메서드 정의</h4>
<h4 id="-프로토타입-메서드">• 프로토타입 메서드</h4>
<p>생성자 함수에서 프로토타입 메서드를 생성하기 위해 명시적으로 <code>prototype</code> 프로퍼티에 추가해야 하지만, 클래스에서 정의한 메서드는 <code>prototype</code> 프로퍼티에 추가하지 않아도 자동으로 프로토타입 메서드가 된다.</p>
<pre><code class="language-js">class User{
  constructor({name, age}) {
    this.name = name;
    this.age = age;
  }
  // 프로토타입 메서드
  sayHello() {
    return `Hello, ${this.name}!`;
  }
}

const user = new User({name: &#39;Lee&#39;, age: 13});
console.log(user.sayHello());

// Hello, Lee!</code></pre>
<h4 id="-정적-메서드">• 정적 메서드</h4>
<p>정적 메서드는 인스턴스를 생성하지 않아도 호출할 수 있는 메서드이다. 클래스에서 <code>static</code> 키워드를 붙이면 정적 메서드가 된다. 정적 메서드는 프로토타입이 아닌 클래스에 바인딩 된 메서드가 되기 때문에 인스턴스로 호출하지 않고 클래스로 호출한다.</p>
<pre><code class="language-js">class User{
  constructor({name, age}) {
    this.name = name;
    this.age = age;
  }
  // 프로토타입 메서드
  sayHello() {
    return `Hello, ${this.name}!`;
  }
  // 정적 메서드
  static welcome() {
    return &#39;Welcome to our shop.&#39;;
  }
}

const user = new User({name: &#39;Lee&#39;, age: 13});
console.log(user.sayHello()); // Hello, Lee!

// 정적 메서드를 인스턴스로 호출하면 에러
console.log(user.welcome()); // Uncaught TypeError: user.welcome is not a function

// 정적 메서드는 인스턴스 없이 호출
console.log(User.welcome()); // Welcome to our shop.</code></pre>
<p>프로토타입 메서드와 정적 메서드는 다음과 같은 차이가 있다.</p>
<ul>
<li>자신이 속해 있는 프로토타입 체인이 다르다.</li>
<li>프로토타입 메서드는 인스턴스로 호출하고, 정적 메서드는 클래스로 호출한다.</li>
<li>프로토타입 메서드는 인스턴스 프로퍼티를 참조할 수 있지만, 정적 메서드는 인스턴스 프로퍼티를 참조할 수 없다. </li>
</ul>
<h3 id="클래스-상속">클래스 상속</h3>
<h4 id="extends-키워드">extends 키워드</h4>
<p><code>extends</code> 키워드는 클래스의 자식 클래스를 생성하기 위해 사용된다.</p>
<pre><code class="language-js">// 수퍼(부모)클래스
class Parent{}

// 서브(자식)클래스
// extends 키워드로 상속
class Child extends Parent{}</code></pre>
<p>상속을 통해 확장된 클래스를 서브 클래스라 하고, 서브 클래스에게 상속한 클래스를 수퍼 클래스라 한다. 프로토타입 메서드, 정적 메서드 모두 상속이 가능하다. 자식 클래스에서 부모 클래스의 속성, 메서드를 사용할 수 있게 된다.</p>
<pre><code class="language-js">class Animal{
  constructor(name, age) {
    this.name = name;
  }

  sleep() {
    return `${this.name} is sleeping.`;
  }
}

// Animal로부터 상속
class Cat extends Animal{
  cry() {
    return `${this.name} is crying.`;
  }
}

let myCat = new Cat(&#39;My cat&#39;);

console.log(myCat.sleep()); // My cat is sleeping.
console.log(myCat.cry()); // My cat is crying.</code></pre>
<p><code>extends</code> 키워드는 프로토타입 기반으로 동작한다. 위의 예제에서 <code>Cat.prototype.[[Prototype]]</code>을 <code>Animal.prototype</code>으로 설정하고, <code>Cat.prototype</code>에서 메서드를 찾지 못하면 <code>Animal.prototype</code>에서 메서드를 가져온다.</p>
<h4 id="super-키워드">super 키워드</h4>
<p><code>super</code> 키워드는 함수처럼 호출할 수도 있고, this와 같이 식별자처럼 참조할 수 있는 키워드이다. <code>super</code> 키워드를 사용하면 부모 클래스의 생성자 함수나 메서드를 호출할 수 있다. 
자식 클래스는 자신이 직접 인스턴스를 생성하지 않고 자식 클래스의 생성자 함수에서 <code>super</code>을 호출하여 부모 클래스에게 인스턴스 생성을 위임한다. 자식 클래스의 생성자 함수에서 <code>super</code>을 호출하기 전에는 <code>this</code>를 참조할 수 없다.</p>
<pre><code class="language-js">class Animal{
  constructor(name, age) {
    this.name = name;
  }
}

class Cat extends Animal{
  constructor(name) {
    super(name); // super 함수 호출로 부모 클래스 생성자 함수 호출
    this.age = age;
  }
}

let myCat = new Cat(&#39;My cat&#39;, 5);

console.log(myCat.name); // My cat
console.log(myCat.age); // 5</code></pre>
<h2 id="정리">정리</h2>
<ul>
<li>클래스 = 붕어빵 틀, 객체 = 붕어빵</li>
<li>class 키워드를 사용해 클래스 생성하고, new 연산자와 함께 호출하면 내부에서 정의한 메서드가 들어있는 객체가 생성된다.</li>
<li>constructor 내부에서 인스턴스의 생성 및 초기화가 이루어진다.</li>
<li>extends, super 키워드를 사용하여 상속한다.</li>
</ul>
<br>
<br>
<br>
참고

<p>모던 자바스크립트 Deep Dive (도서)
<a href="https://ko.javascript.info/class">https://ko.javascript.info/class</a>
<a href="https://ko.javascript.info/class-inheritance">https://ko.javascript.info/class-inheritance</a>
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes">https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL]JavaScript_클로저]]></title>
            <link>https://velog.io/@florence_y/TIL-5ep7173c</link>
            <guid>https://velog.io/@florence_y/TIL-5ep7173c</guid>
            <pubDate>Wed, 14 Jun 2023 14:35:08 GMT</pubDate>
            <description><![CDATA[<h2 id="클로저closure">클로저(Closure)</h2>
<p>클로저는 함수와 해당 함수가 선언된 렉시컬 환경의 조합이다. 외부 함수의 실행이 종료된 후에도 중첩 함수는 외부 함수의 변수를 기억하여 참조할 수 있다. 이러한 중첩 함수를 클로저라고 한다. </p>
<blockquote>
<p>📌 <strong>렉시컬 스코프</strong>
함수가 정의된 위치에 의해 상위 스코프가 결정되는 스코프</p>
</blockquote>
<h3 id="함수-객체의-내부-슬롯-environment">함수 객체의 내부 슬롯 [[Environment]]</h3>
<p>함수 객체를 생성할 때 자신이 정의된 위치에 의해 결정된 상위 스코프의 참조를 자신의 내부 슬롯 [[Environment]]에 저장한다. </p>
<pre><code class="language-javascript">const x = 1;

function funcOne() {
    const x = 5;

// 함수의 호출 위치와 상위 스코프는 아무런 관계가 없다.
    funcTwo();
};

function funcTwo() {
  // 함수 funcTwo는 자신의 상위 스코프를 내부슬롯 [[Environment]]에 저장하여 기억한다.
 console.log(x);
};

funcOne(); // 1
funcTwo(); // 1</code></pre>
<h3 id="클로저와-렉시컬-환경">클로저와 렉시컬 환경</h3>
<p>외부 함수보다 중첩 함수가 더 오래 유지되는 경우 중첩 함수는 이미 생명 주기가 종료한 외부 함수의 변수를 참조할 수 있다.</p>
<pre><code class="language-javascript">const x = 1;

function outer() {
    const x = 5;

      // 중첩 함수 inner은 클로저
      const inner = function() {
      console.log(x);
    };
    return inner;
};

// 함수 outer을 호출하면 중첩 함수 inner을 반환하고, outer 함수의 실행 컨텍스트는 실행 컨텍스트 스택에서 제거된다.
const innerFunc = outer();
innerFunc(); // 5</code></pre>
<p>위의 예제에서, 함수 outer의 실행 컨텍스트는 실행 컨텍스트 스택에서 제거되지만, outer의 렉시컬 환경까지 소멸하는 것은 아니다. 함수 outer의 렉시컬 환경은 함수 inner의 [[Environment]] 내부 슬롯에 의해 참조되고 있고 inner은 전역 변수 innerFunc에 의해 참조되고 있다.
📢 클로저에 의해 참조되는 상위 스코프의 변수(예제에서 outer 함수의 x)를 <code>자유 변수</code>라고 부른다. 클로저란 자유 변수에 묶여있는 함수라고도 할 수 있다.</p>
<h3 id="클로저의-활용">클로저의 활용</h3>
<p><strong>상태(state)를 안전하게 은닉, 특정 함수에게만 상태 변경 허용</strong>
함수가 호출될 때마다 횟수를 누적하여 출력하는 카운터를 만들어보자.</p>
<pre><code class="language-javascript">let num = 0;

const increase = function() {
    return num++;
};

console.log(increase()); // 1
console.log(increase()); // 2
console.log(increase()); // 3</code></pre>
<p>카운트 상태(num)는 increase 함수가 호출되기 전까지 변경되지 않아야 하고, 카운트 상태(num)는 increase 함수만이 변경할 수 있어야 한다. 하지만 위의 예제에서 카운트 상태가 전역에서 관리되고 있으므로 누구나 접근할 수 있고 변경할 수 있는 위험이 있다.</p>
<pre><code class="language-javascript">// 카운트 상태 변경 함수
const increase = (function() {
      let num = 0;
      // 클로저
    return function() {
        return num++
    };
}());

console.log(increase()); // 1
console.log(increase()); // 2
console.log(increase()); // 3</code></pre>
<p>위의 예제에서, 즉시 실행 함수가 반환한 클로저는 카운트 상태를 유지하기 위한 자유 변수 num을 어디서 호출하든지 참조하고 변경할 수 있다. 또한 num 변수는 은닉된 private 변수이므로 의도되지 않은 변경을 걱정하지 않아도 된다.</p>
<h2 id="정리">정리</h2>
<ul>
<li>클로저는 외부 함수의 실행이 종료된 후에도 외부 함수의 변수를 기억하여 참조할 수 있는 함수이다.</li>
<li>외부 함수보다 중첩 함수가 더 오래 유지되는 경우 중첩 함수는 이미 생명 주기가 종료한 외부 함수의 변수를 참조할 수 있다.</li>
<li>클로저는 상태(state)를 은닉하고 특정 함수에게만 상태 변경을 허용하여 상태를 안전하게 변경하고 유지하기 위해 사용한다.</li>
</ul>
<br>
<br>
<br>

<p>참고</p>
<p>모던 자바스크립트 Deep Dive (도서)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL]JavaScript_반복문]]></title>
            <link>https://velog.io/@florence_y/TIL-a0p5kqsc</link>
            <guid>https://velog.io/@florence_y/TIL-a0p5kqsc</guid>
            <pubDate>Thu, 08 Jun 2023 13:54:33 GMT</pubDate>
            <description><![CDATA[<p>자바스크립트는 반복문인 <code>for</code> 문, <code>while</code> 문, <code>do... while</code> 문을 제공한다. 그 외에도 반복문을 대체할 수 있는 다양한 기능을 제공한다. </p>
<h2 id="반복문loop">반복문(Loop)</h2>
<h3 id="for-문">for 문</h3>
<p>for 문은 조건식이 거짓으로 평가될 때까지 코드 블록을 반복 실행한다. </p>
<pre><code class="language-jsx">for(초기화식; 조건식; 증감식) {
    조건식이 참인 경우 반복할 코드;
}</code></pre>
<p>for 문의 괄호 안에는 세미콜론으로 구분되는 세 가지 항목이 있다. </p>
<pre><code class="language-jsx">for(let i = 0; i &lt; 2; i++) {
    console.log(i);
}

// 0
// 1</code></pre>
<p>위의 예시를 살펴보면, 가장 먼저 초기화식인 <code>let i = 0;</code> 이 실행된다. 그 후 <code>i &lt; 2;</code>라는 조건식이 실행되는데, 현재 i의 값이 0이므로 조건식의 결과는 true이므로 <code>console.log(i)</code> 코드가 실행된다. 코드 실행이 종료되면 증감식 <code>i++</code>가 실행되어 i의 값은 1이 된다. 이렇게 반복하다가 조건식의 평가가 false일 때, 즉 i의 값이 2가 될 때 for 문은 종료된다. </p>
<p>📌 for 문에서 초기화식, 조건식, 증감식 모두 생략한다면 무한루프가 된다. </p>
<pre><code class="language-jsx">for(;;) {
    // 코드가 무한 반복됨
}</code></pre>
<h4 id="👉-중첩-for-문">👉 중첩 for 문</h4>
<p>for 문 안에 for 문을 중첩해서 사용할 수 있다. </p>
<pre><code class="language-jsx">for(let i = 1; i &lt; 10; i++) {
    for(let j = 1; j &lt; 10; j++) {
        console.log(i, j, i*j);
    }
}

// 1 1 1
// 1 2 2
// 1 3 3
// 1 4 4
// 1 5 5
// 1 6 6
// 1 7 7
// 1 8 8
// 1 9 9
// ...
// 9 7 63
// 9 8 72
// 9 9 81</code></pre>
<p>예시로 구구단을 출력해보았다. 
&nbsp;&nbsp;    1. 첫 번째 for 문의 조건식을 실행시켜 두 번째 for 문으로 들어가고, 두 번째 for 문이 실행된다. 
&nbsp;&nbsp;    2. 두 번째 for 문의 조건이 모두 완료되면 첫 번째 for 문으로 돌아가고, 두 번째 for 문의 조건식은 초기화된다. 
&nbsp;&nbsp;    3. 이 과정을 반복하여 첫 번째 for 문의 조건식이 false가 되면 for 문은 종료된다. </p>
<h3 id="while-문">while 문</h3>
<p>while 문은 조건식의 평가 결과가 true이면 코드 블록을 반복 실행한다. for 문은 반복 횟수가 명확할 때 사용하고 while 문은 반복 횟수가 불명확할 때 주로 사용한다.</p>
<pre><code class="language-jsx">while(조건식) {
    // 조건식이 참이면 반복할 코드
}</code></pre>
<p>조건문의 평가 결과가 false가 되면 코드 블록을 실행하지 않고 종료한다.</p>
<pre><code class="language-jsx">let i = 0;

while(i &lt; 3) {
    console.log(i);
      i++;
}

// 0
// 1
// 2</code></pre>
<p>📌 while 문에서 조건식의 평가 결과가 언제나 true이면 무한루프가 된다. </p>
<pre><code class="language-jsx">while(true) {
    // 코드가 무한 반복됨
}</code></pre>
<p>무한루프에서 탈출하려면 코드 블록 내에 if 문으로 탈출 조건을 만들고 break 문을 사용한다. </p>
<pre><code class="language-jsx">let i = 0;

while(true) {
  console.log(i);
  i++;
  if(i === 3) break; // i의 값이 3이 되면 반복문을 빠져나온다.
}

// 0 
// 1
// 2</code></pre>
<h3 id="do-while-문">do... while 문</h3>
<p>do... while 문은 코드 블록을 먼저 실행하고 조건식을 평가한다.</p>
<pre><code class="language-jsx">do {
    // 실행할 코드
} while(조건식);</code></pre>
<p>코드가 먼저 실행되고, 조건식의 평가 결과가 true인 동안에 코드는 계속 실행된다. do... while 문은 조건식의 평가 결과에 상관없이 코드 블록을 최소한 한 번이라도 실행하고 싶을 때 사용한다.</p>
<pre><code class="language-jsx">let i = 0;

do {
  console.log(i);
  i++; // i의 값이 3보다 작으면 계속 실행된다.
} while(i &lt; 3);

// 0 
// 1
// 2</code></pre>
<h3 id="반복문-탈출하기---break-문">반복문 탈출하기 - break 문</h3>
<p>while 문에서 살펴봤듯이 break  문은 코드 블록을 탈출한다. 
중첩 for 문의 내부 for 문에서 break를 실행하면 내부 for 문을 탈출하고 외부 for 문으로 진입하는데, 외부 for 문까지 탈출하려면 레이블 문을 사용한다.
🔎 레이블 문: 식별자가 붙은 문</p>
<pre><code class="language-jsx">outer: for(let i = 0; i &lt; 3; i++) {
    for(let j = 0; j &lt; 3; j++) {
        if(i + j === 3) break outer; // i + j === 3이면 outer 탈출
          console.log(`${i}, ${j});
    }
}

console.log(&#39;Done!&#39;);

// 0, 0
// 0, 1
// 0, 2
// 1, 0
// 1, 1
// Done!</code></pre>
<p>📢 레이블 문은 중첩 for 문을 탈출할 때 유용하지만 그 밖의 경우에는 권장하지 않는다. 레이블 문을 사용하면 프로그램의 흐름이 복잡해져서 가독성이 나빠지고 오류를 발생시킬 가능성이 커지기 때문이다. </p>
<h3 id="다음-반복으로-넘어가기---continue-문">다음 반복으로 넘어가기 - continue 문</h3>
<p>continue 문은 현재 진행 중인 반복을 멈추고 바로 다음 반복을 실행시킨다. break 문처럼 반복문을 탈출하지는 않는다. </p>
<pre><code class="language-jsx">for (let i = 0; i &lt; 5; i++) {
    if (i === 3) continue; // i의 값이 3이면 실행을 중단하고 반복문의 증감식으로 이동한다.
      console.log(i);
}

// 0
// 1
// 2
// 4</code></pre>
<h2 id="배열-메서드">배열 메서드</h2>
<h3 id="foreach">forEach</h3>
<p>forEach 메서드는 Array(배열) 객체에서 사용할 수 있는 메서드이다. 배열을 순회하면서 수행해야 할 처리를 콜백 함수로 전달받아 반복 호출한다. </p>
<pre><code class="language-jsx">const numbers = [1, 2, 3];
const result = [];

// numbers 배열의 모든 요소를 순회하면서 콜백 함수를 반복 호출
numbers.forEach(item =&gt; result.push(item + 1));
console.log(result);

// [2, 3, 4]</code></pre>
<p>forEach 메서드는 콜백 함수를 호출할 때 배열의 요소값과 인덱스, 호출한 배열을 순차적으로 전달한다. </p>
<pre><code class="language-jsx">const number = [1, 2, 3];

numbers.forEach((item, index, arr) =&gt; {
    console.log(item, index, arr);
});

// 1 0 [1, 2, 3]
// 2 1 [1, 2, 3]
// 3 2 [1, 2, 3]
</code></pre>
<p>forEach 메서드는 break, continue 문을 사용할 수 없고, 배열의 모든 요소를 순회한다.</p>
<h3 id="map">map</h3>
<p>map 메서드는 forEach 메서드처럼 배열의 모든 요소를 순회하면서 콜백 함수를 반복 호출한다. 둘의 차이점은 forEach 메서드는 단순히 반복문을 대체하기 위한 메서드이고, map 메서드는 요소값을 다른 값으로 매핑한 새로운 배열을 생성한다는 점이다. </p>
<pre><code class="language-jsx">// map 사용
const numbers = [1, 2, 3];

const result = numbers.map(item =&gt; item + 1);
console.log(result);

// [2, 3, 4]</code></pre>
<pre><code class="language-jsx">// forEach 사용
const numbers = [1, 2, 3];

const result = numbers.forEach(item =&gt; item + 1);

console.log(result);

// undefined</code></pre>
<p>같은 코드를 map 메서드와 forEach 메서드를 사용하여 출력하였다. map 메서드는 배열의 모든 요소에 콜백 함수를 실행시켜 새로운 배열을 반환한다는 점을 알 수 있다.</p>
<h3 id="filter">filter</h3>
<p>filter 메서드 또한 배열의 모든 요소를 순회하면서 콜백 함수를 반복 호출한다. 하지만 콜백 함수의 반환값이 true인 요소들만 추출한 새로운 배열을 반환한다. </p>
<pre><code class="language-jsx">const numbers = [1, 2, 3, 4, 5, 6];

const result = numbers.filter(item =&gt; item % 2 === 0);
console.log(result);

// [2, 4, 6]</code></pre>
<h3 id="reduce">reduce</h3>
<p>reduce 메서드는 콜백 함수의 반환값을 다음 순회 시 콜백 함수의 첫 번째 인수로 전달하면서 콜백 함수를 호출하여 하나의 결과값을 만들어 반환한다. 
<br>
reduce 메서드는 첫 번째 인수로 콜백 함수, 두 번째 인수로 초기값을 전달받는데, 콜백 함수에는 초기값(콜백 함수의 이전 반환값), 처리할 요소값, 인덱스, reduce 메서드를 호출한 배열이 전달된다.</p>
<pre><code class="language-jsx">const result = [1, 2, 3, 4].reduce((accumulator, currentValue, index, array) =&gt; accumulator + currentValue, 0);

console.log(result);

// 10 (0(초기값) + 1 + 2 + 3 + 4)</code></pre>
<p>reduce 메서드의 두 번째 인수인 초기값은 생략할 수도 있지만, 에러가 발생할 수 있으니 언제나 초기값을 전달하는 것이 안전하다.</p>
<h2 id="객체-프로퍼티-열거">객체 프로퍼티 열거</h2>
<h3 id="for-in-문">for ...in 문</h3>
<p>객체의 모든 프로퍼티를 순회하며 열거하려면 for ...in 문을 사용한다.</p>
<pre><code class="language-jsx">const city = {
    name : &#39;Seoul&#39;,
      code : 11
}

// city 객체의 프로퍼티를 모두 순회하면서 프로퍼티 키를 key 변수에 할당한 후 코드를 실행
for (const key in city) {
    console.log(`${key} : ${city[key]}`);
}

// name : Seoul
// code : 11</code></pre>
<p>for ...in 문은 객체의 프로퍼티 뿐만 아니라 상속받은 프로토타입의 프로퍼티까지 열거한다. 하지만 프로퍼티 어트리뷰트 [[Enumerable]]의 값이 true인 프로퍼티만 순회하며 열거한다.</p>
<h2 id="객체-메서드">객체 메서드</h2>
<p>객체 자신의 고유 프로퍼티만 열거하기 위해서는 Object.keys/values/entries 메서드를 사용한다.</p>
<h3 id="objectkeys">Object.keys</h3>
<p>Object.keys 메서드는 객체 자신의 열거 가능한 프로퍼티 키를 배열로 반환한다.</p>
<pre><code class="language-jsx">const city = {
    name : &#39;Seoul&#39;,
      code : 11
}

console.log(Object.keys(city));

//  [&#39;name&#39;, &#39;code&#39;]</code></pre>
<h3 id="objectvalues">Object.values</h3>
<p>Object.values 메서드는 객체 자신의 열거 가능한 프로퍼티 값을 배열로 반환한다.</p>
<pre><code class="language-jsx">const city = {
    name : &#39;Seoul&#39;,
      code : 11
}

console.log(Object.values(city));

//  [&#39;Seoul&#39;, 11]</code></pre>
<h3 id="objectentries">Object.entries</h3>
<p>Object.entries 메서드는 객체 자신의 열거 가능한 프로퍼티 [키, 값]을 배열로 반환한다.</p>
<pre><code class="language-jsx">const city = {
    name : &#39;Seoul&#39;,
      code : 11
}

console.log(Object.entries(city));

// [[&#39;name&#39;, &#39;Seoul&#39;], [&#39;code&#39;, 11]]</code></pre>
<h2 id="이터러블-순회">이터러블 순회</h2>
<h3 id="for-of-문">for ...of 문</h3>
<p>for ...of 문은 ES6에서 도입된 문법으로 이터러블을 순회하면서 이터러블의 요소를 변수에 할당한다. for ...of 문은 이터러블 객체(자료를 반복할 수 있는 객체)에서만 사용할 수 있다. 배열, 문자열, Map, Set 등이 이터러블 객체이다.</p>
<pre><code class="language-jsx">const number = [1, 2, 3];

for (const item of number) {
      //item 변수에 1, 2, 3이 할당
      console.log(item);
}

// 1
// 2
// 3</code></pre>
<h2 id="정리">정리</h2>
<ul>
<li><code>for 문</code> : 조건식이 거짓으로 평가될 때까지 코드 블록을 반복 실행</li>
<li><code>while 문</code> : 조건식의 평가 결과가 true이면 코드 블록을 반복 실행. 반복 횟수가 명확할 때 for 문 사용, 반복 횟수가 불명확할 때 while 문 사용</li>
<li><code>do... while</code> 문 : 코드 블록을 먼저 실행하고 조건식을 평가. 조건식의 평가 결과에 상관없이 코드 블록을 최소한 한 번이라도 실행하고 싶을 때 사용</li>
<li><code>break 문</code> : 코드 블록 탈출 / <code>continue 문</code> : 현재 진행 중인 반복 멈추고 바로 다음 반복 실행</li>
<li><code>forEach</code> : 배열 순회하면서 수행해야 할 처리를 콜백 함수로 전달받아 반복 호출</li>
<li><code>map</code> : 배열의 요소값을 다른 값으로 매핑한 새로운 배열 생성</li>
<li><code>filter</code> : 콜백 함수의 반환값이 true인 요소들만 추출한 새로운 배열을 반환</li>
<li><code>reduce</code> : 콜백 함수의 반환값을 다음 순회 시 콜백 함수의 첫 번째 인수로 전달하면서 콜백 함수를 호출하여 하나의 결과값을 만들어 반환</li>
<li><code>for ...in 문</code> :객체의 모든 프로퍼티를 순회하며 열거</li>
<li><code>Object.keys</code> : 객체의 프로퍼티 키를 배열로 반환</li>
<li><code>Object.values</code> : 객체의 프로퍼티 값을 배열로 반환</li>
<li><code>Object.entries</code> : 객체의 프로퍼티 [키, 값]을 배열로 반환</li>
<li><code>for ...of</code> 문 : 이터러블 객체(자료를 반복할 수 있는 객체)에서만 사용</li>
</ul>
<br>
<br>
<br>

<p>참고</p>
<p>모던 자바스크립트 Deep Dive (도서)</p>
<p><a href="https://ko.javascript.info">https://ko.javascript.info</a></p>
<p><a href="https://developer.mozilla.org">https://developer.mozilla.org</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL]JavaScript_조건문]]></title>
            <link>https://velog.io/@florence_y/TIL-ok0nynsu</link>
            <guid>https://velog.io/@florence_y/TIL-ok0nynsu</guid>
            <pubDate>Mon, 05 Jun 2023 11:34:06 GMT</pubDate>
            <description><![CDATA[<h2 id="조건문conditional-statement">조건문(Conditional Statement)</h2>
<p>자바스크립트에서 조건문은 주어진 조건식의 평가 결과에 따라 코드 블록의 실행을 결정한다. </p>
<h3 id="if문">if문</h3>
<p>if문은 괄호 안에 들어가는 조건이 true이면 코드 블록이 실행된다. </p>
<pre><code class="language-javascript">if(조건식){
  // 조건이 참이면 실행될 코드
}</code></pre>
<p>조건이 참인 경우 실행할 코드가 여러개라면, 중괄호<code>{}</code>로 감싸준다.</p>
<pre><code class="language-javascript">if(name === &#39;kim&#39;){
  alert(&#39;안녕하세요.&#39;);
  alert(&#39;반갑습니다.&#39;);
}</code></pre>
<p>📢 조건이 참인 경우 실행할 코드가 한 줄 뿐이라면 중괄호를 생략할 수 있지만, 코드의 가독성을 위해 중괄호로 감싸는 것을 추천한다. </p>
<h3 id="if--else-문">if ... else 문</h3>
<p>if ... else문은 조건식의 평가 결과가 true일 경우 if문의 코드 블록이 실행되고, false인 경우 else문의 코드 블록이 실행된다. </p>
<pre><code class="language-javascript">if(조건식){
  // 조건이 참이면 실행될 코드
} else {
  // 조건이 거짓이면 실행될 코드
}</code></pre>
<pre><code class="language-javascript">if(x % 2){ // x값이 2로 나누어질 경우
  console.log(&#39;짝수&#39;);
} else { // x값이 2로 나누어지지 않은 경우
  console.log(&#39;홀수&#39;);
}</code></pre>
<p>조건 여러개를 처리해야 할 경우 else if 문을 사용한다. if문과 else문은 한 번만 사용 가능하지만 else if문은 여러 번 사용할 수 있다. </p>
<pre><code class="language-javascript">if(조건식1){
  // 조건식1이 참이면 실행될 코드
} else if(조건식2) {
  // 조건식2가 참이면 실행될 코드
} else {
  // 조건식1, 조건식2 모두 거짓이면 실행될 코드
}</code></pre>
<pre><code class="language-javascript">if(x &gt; 0){ // x값이 0보다 클 경우
  console.log(&#39;양수&#39;);
} else if(x &lt; 0) { // x값이 0보다 작을 경우
  console.log(&#39;음수&#39;);
} else { // x값이 0보다 크지도 작지도 않을 경우
  console.log(&#39;영&#39;);
}</code></pre>
<h3 id="조건부-연산자-">조건부 연산자 &#39;?&#39;</h3>
<p>if ... else 문은 조건부 연산자 &#39;?&#39;를 사용해 나타낼 수 있다. </p>
<pre><code class="language-javascript">조건식 ? 조건식이 true일 때 반환할 값 : 조건식이 false일 때 반환할 값</code></pre>
<pre><code class="language-javascript">// x가 2로 나누어지면 짝수, 나누어지지 않으면 홀수
(x % 2) ? &#39;짝수&#39; : &#39;홀수&#39;;</code></pre>
<p>📢 단순히 값을 결정하여 변수에 할당하는 경우 조건부 연산자를 사용하는 편이 가독성이 좋다. 하지만 여러 줄의 문이 필요하다면 if ... else 문을 사용하는 편이 가독성이 좋다.</p>
<h3 id="switch문">switch문</h3>
<p>복수의 if 조건문은 switch 문으로 바꿔 사용할 수 있다. switch 문은 하나 이상의 case 문으로 구성되어 있다. 주어진 표현식을 평가하여 그 값과 일치하는 표현식을 갖는 case 문으로 이동한다. 일치하는 case 문이 없다면 default 문으로 이동하는데, default 문은 필수는 아니다.</p>
<pre><code class="language-javascript">switch(표현식) {
  case 표현식1:
    switch 문의 표현식과 표현식1이 일치하면 실행될 문;
    break;
  case 표현식2:
    switch 문의 표현식과 표현식2이 일치하면 실행될 문;
    break;
  default:
    switch 문의 표현식과 일치하는 case 문이 없을 때 실행될 문;
}</code></pre>
<pre><code class="language-javascript">let month = 5;
let thisMonth;

switch(month) {
  case 1: thisMonth = &#39;Jan&#39;;
    break;
  case 2: thisMonth = &#39;Feb&#39;
    break;
  case 3: thisMonth = &#39;Mar&#39;
    break;
  case 4: thisMonth = &#39;Apr&#39;
    break;
  case 5: thisMonth = &#39;May&#39;
    break;
  case 6: thisMonth = &#39;Jun&#39;
    break;
  case 7: thisMonth = &#39;Jul&#39;
    break;
  case 8: thisMonth = &#39;Aug&#39;
    break;
  case 9: thisMonth = &#39;Sep&#39;
    break;
  case 10: thisMonth = &#39;Oct&#39;
    break;
  case 11: thisMonth = &#39;Nov&#39;
    break;
  case 12: thisMonth = &#39;Dec&#39;
    break;
  default: thisMonth = &#39;Invalid month&#39;;
}

console.log(thisMonth); // Jun</code></pre>
<p>switch 문에서 break 문을 사용하지 않으면 조건에 부합하는지 확인하지 않고 다음 case 문으로 연이어 이동하기 때문에 break 문을 꼭 사용해야 한다. </p>
<br>
<br>
<br>

<p>참고</p>
<p>모던 자바스크립트 Deep Dive (도서)</p>
<p><a href="https://ko.javascript.info/ifelse">https://ko.javascript.info/ifelse</a></p>
<p><a href="https://ko.javascript.info/switch">https://ko.javascript.info/switch</a></p>
<p><a href="https://developer.mozilla.org">https://developer.mozilla.org</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL]JavaScript_객체]]></title>
            <link>https://velog.io/@florence_y/TIL</link>
            <guid>https://velog.io/@florence_y/TIL</guid>
            <pubDate>Fri, 02 Jun 2023 14:21:21 GMT</pubDate>
            <description><![CDATA[<h2 id="객체object">객체(Object)</h2>
<p>객체란 키(key)와 값(value)으로 구성된 프로퍼티의 집합이다. 여러 속성(property)을 하나의 변수에 담는 데이터 구조이다. 자바스크립트에서는 함수, 배열 등 대부분의 것들이 객체이다. </p>
<pre><code class="language-javascript">let member = {
    name: &#39;Kim&#39;,
      age: 20
};</code></pre>
<p>member라는 변수 안에 <code>{ }</code>를 사용하여 객체를 표현하였다. 
<img src="https://velog.velcdn.com/images/florence_y/post/d45976b4-15fe-45f1-813a-ca2cc89a4767/image.svg" alt=""></p>
<h3 id="프로퍼티">프로퍼티</h3>
<p>위의 코드에서 객체 member의 프로퍼티는 <code>name: &#39;Kim&#39;</code> 과 <code>age: 20</code>이다. 객체의 상태를 나타내는 값을 프로퍼티라 하며, 콤마<code>(,)</code>로 구분한다. </p>
<h3 id="키와-값">키와 값</h3>
<p>콜론<code>(:)</code>을 기준으로 왼쪽은 키(key), 오른쪽은 값(value)이다. 
위의 코드에서 <code>name</code>과 <code>age</code>는 키(key)이고, <code>&#39;Kim&#39;</code>과 <code>20</code>은 값(value)이다. </p>
<h4 id="프로퍼티-키">프로퍼티 키</h4>
<p>프로퍼티 키는 프로퍼티 값에 접근할 수 있는 이름으로서 식별자 역할을 한다. 프로퍼티 키에는 빈 문자열을 포함한 모든 문자열 또는 심벌 값을 사용할 수 있다. </p>
<ul>
<li>키는 식별자 네이밍 규칙을 따르지 않는다면 반드시 따옴표<code>(&#39;&#39;)</code>를 사용해야 한다. </li>
</ul>
<pre><code class="language-javascript">let member = {
    firstName: &#39;Jenny&#39;,
      &#39;last-name&#39;: &#39;Kim&#39;
};</code></pre>
<ul>
<li>따옴표를 생략한다면 자바스크립트 엔진은 last-name을 <code>-</code> 연산자가 있는 표현식으로 해석한다. </li>
</ul>
<pre><code class="language-javascript">let member = {
    firstName: &#39;Jenny&#39;,
      last-name: &#39;Kim&#39;  // Uncaught SyntaxError: Unexpected token &#39;-&#39;
};</code></pre>
<ul>
<li>프로퍼티 키에 문자열이나 심벌 값 외의 값을 사용하면 암묵적 타입 변환을 통해 문자열이 된다. 예를 들어, 숫자 리터럴을 사용하면 문자열로 반환된다. </li>
</ul>
<pre><code class="language-javascript">let number = {
    0: 10,
      1: 20,
      2: 30
};

console.log(number)  // {0: 10, 1: 20, 2: 30}</code></pre>
<ul>
<li>이미 존재하는 프로퍼티 키를 중복 선언하면 나중에 선언한 프로퍼티가 먼저 선언한 프로퍼티를 덮어쓴다. </li>
</ul>
<pre><code class="language-javascript">let member = {
    name: &#39;Kim&#39;,
      name: &#39;Lee&#39;
};

console.log(member)  // {name: &#39;Lee&#39;}</code></pre>
<p>📢 var, function과 같은 예약어를 프로퍼티 키로 사용해도 되지만, 예상치 못한 에러가 발생할 수 있으므로 권장하지 않는다. </p>
<h4 id="프로퍼티-값">프로퍼티 값</h4>
<p>프로퍼티 값은 문자열, 숫자, 함수 등 자바스크립트에서 사용할 수 있는 모든 값이 들어올 수 있다. 이 중 프로퍼티 값이 함수일 경우 <strong>메서드</strong>라 부른다. </p>
<pre><code class="language-javascript">let member = {
    name: &#39;Kim&#39;,
      message: function(){  // 여기서 message가 메서드
        console.log(&#39;안녕하세요&#39;);
    }
};</code></pre>
<h3 id="객체-생성">객체 생성</h3>
<p>자바스크립트에서의 객체 생성 방법은 여러가지가 있다. </p>
<ul>
<li>객체 리터럴</li>
<li>Object 생성자 함수</li>
<li>생성자 함수</li>
<li>Object.create 메서드</li>
<li>클래스</li>
</ul>
<pre><code class="language-javascript">let member = {};  // 객체 리터럴
let member = new Object();  // Object 생성자 함수</code></pre>
<p>이 방법 중 가장 일반적이고 간단한 방법은 객체 리터럴을 사용하는 방법이다. </p>
<h3 id="프로퍼티-접근">프로퍼티 접근</h3>
<p>프로퍼티에 접근하는 방법은 <code>.</code>를 사용하는 마침표 표기법(dot notation)과 
<code>[]</code>를 사용하는 대괄호 표기법(bracket notation)이 있다. </p>
<p><strong>마침표 표기법(dot notation)</strong></p>
<pre><code class="language-javascript">let member = {
    name: &#39;Kim&#39;
};

console.log(member.name); // Kim</code></pre>
<p><strong>대괄호 표기법(dot notation)</strong>
대괄호 표기법에서 대괄호 내부에 지정하는 프로퍼티 키는 반드시 <code>따옴표(&#39;&#39;)</code>로 감싼 문자열이어야 한다. 따옴표로 감싸지 않으면 식별자로 해석하기 때문이다. </p>
<pre><code class="language-javascript">let member = {
    name: &#39;Kim&#39;
};

console.log(member[&#39;name&#39;]); // Kim

console.log(member[name]); // undefined
</code></pre>
<p>프로퍼티 키가 식별자 네이밍 규칙을 준수하지 않는 이름이라면, 프로퍼티 접근 시 반드시 대괄호 표기법을 사용해야 한다. </p>
<pre><code class="language-javascript">let member = {
    &#39;last-name&#39;: &#39;Kim&#39;
};

console.log(member.&#39;last-name&#39;); // Uncaught SyntaxError: Unexpected string
console.log(member.last-name); // NaN

console.log(member[last-name]); // Uncaught ReferenceError: last is not defined
console.log(member[&#39;last-name&#39;]; // Kim
</code></pre>
<p>📍 member.last-name을 실행하면 자바스크립트 엔진은 먼저 member.last를 평가한다. member 객체에는 프로퍼티 키가 last인 프로퍼티가 없기 때문에 member.last는 <code>undefined</code>로 평가된다. 다음으로 자바스크립트 엔진은 name이라는 식별자를 찾는데, 어디에도 name이라는 식별자 선언이 없으므로 <code>ReferenceError</code>가 발생한다. 그런데 브라우저 환경에는 name이라는 전역 변수가 암묵적으로 존재하는데, 전역변수 name의 기본값은 빈 문자열이다. 따라서 <code>member.last-name</code>은 <code>undefinded-&#39;&#39;</code>와 같으므로 <code>NaN</code>을 출력한다. </p>
<h4 id="프로퍼티-값-갱신"><strong>프로퍼티 값 갱신</strong></h4>
<p>이미 존재하는 프로퍼티에 값을 할당하면 프로퍼티 값이 갱신된다.</p>
<pre><code class="language-javascript">let member = {
    name: &#39;Kim&#39;
};

member.name = &#39;Lee&#39;;

console.log(member); // {name: &#39;Lee&#39;}</code></pre>
<h4 id="프로퍼티-값-동적-생성"><strong>프로퍼티 값 동적 생성</strong></h4>
<p>존재하지 않는 프로퍼티에 값을 할당하면 프로퍼티가 동적으로 생성된다. </p>
<pre><code class="language-javascript">let member = {
    name: &#39;Kim&#39;
};

member.age = 20;

console.log(member); // {name: &#39;Kim&#39;, age: 20}</code></pre>
<h4 id="프로퍼티-값-삭제"><strong>프로퍼티 값 삭제</strong></h4>
<p><code>delete</code> 연산자를 사용하여 프로퍼티 값을 삭제한다. 이 때 delete 연산자의 피연산자는 프로퍼티 값에 접근할 수 있는 표현식이어야 한다. 존재하지 않는 프로퍼티를 삭제해도 에러가 나지 않는다. </p>
<pre><code class="language-javascript">let member = {
    name: &#39;Kim&#39;
};

member.age = 20;

delete member.age;

delete member.number; // 에러 X

console.log(member); // {name: &#39;Kim&#39;}</code></pre>
<h2 id="정리">정리</h2>
<ul>
<li><p>객체란 <code>키(key)</code>와 <code>값(value)</code>으로 구성된 프로퍼티의 집합이다. </p>
</li>
<li><p>프로퍼티 키에는 빈 문자열을 포함한 모든 <code>문자열</code> 또는 <code>심벌 값</code>을 사용할 수 있다.</p>
</li>
<li><p>프로퍼티 값은 문자열, 숫자, 함수 등 자바스크립트에서 사용할 수 있는 모든 값이 들어올 수 있다. </p>
</li>
<li><p>프로퍼티 값이 함수일 경우 <code>메서드</code>라 부른다.</p>
</li>
<li><p>마침표 표기법 <code>obj.property</code></p>
</li>
<li><p>대괄호 표기법 <code>obj[&#39;property&#39;]</code>. 프로퍼티 키가 식별자 네이밍 규칙을 준수하지 않는 이름이라면, 프로퍼티 접근 시 반드시 대괄호 표기법을 사용해야 한다.</p>
</li>
<li><p>이미 존재하는 프로퍼티에 값을 할당하면 프로퍼티 값이 갱신된다.</p>
</li>
<li><p>존재하지 않는 프로퍼티에 값을 할당하면 프로퍼티가 동적으로 생성된다.</p>
</li>
<li><p><code>delete</code> 연산자를 사용하여 프로퍼티 값을 삭제한다.</p>
</li>
</ul>
<br>
<br>
<br>

<p>*<em>참고 *</em></p>
<p>모던 자바스크립트 Deep Dive (도서)</p>
<p><a href="https://ko.javascript.info/variables">https://ko.javascript.info/variables</a></p>
<p><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Basics">https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Basics</a></p>
<br>]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL]JavaScript_var, let, const 비교하기]]></title>
            <link>https://velog.io/@florence_y/TILJavaScriptvar-let-const-%ED%8C%8C%ED%97%A4%EC%B9%98%EA%B8%B0</link>
            <guid>https://velog.io/@florence_y/TILJavaScriptvar-let-const-%ED%8C%8C%ED%97%A4%EC%B9%98%EA%B8%B0</guid>
            <pubDate>Thu, 01 Jun 2023 14:02:43 GMT</pubDate>
            <description><![CDATA[<p>변수를 선언하려면 <code>var</code>, <code>let</code>, <code>const</code> 키워드가 필요하다. 변수를 선언한다는 공통점이 있지만, 서로 다른 특징을 가지고 있다. 
개발자라면 필수적으로 알아야 할 <code>var</code>, <code>let</code>, <code>const</code>의 차이점을 자세히 다루어보려 한다. </p>
<h2 id="변수-선언-시-수행-단계">변수 선언 시 수행 단계</h2>
<p>세가지 키워드를 알아보기 전, 변수 선언 시 변수 선언을 하면 어떤 단계를 거치는지 알아보자. </p>
<ul>
<li>선언 단계: <strong>변수 이름을 등록</strong>해서 자바스크립트 엔진에 변수의 존재를 알린다.
만약 선언하지 않은 식별자에 접근하면 ReferenceError가 발생한다. <pre><code class="language-javascript">var member;</code></pre>
</li>
<li>초기화 단계: 값을 저장하기 위한 <strong>메모리 공간을 확보</strong>하고 암묵적으로 <strong>undefined</strong>를 할당해 초기화한다. 
→ 초기화: 변수가 선언된 이후 최초로 값을 할당하는 것 <pre><code class="language-javascript">var member;
console.log(member); // undefined
// 어떠한 값도 할당하지 않았지만 undefined가 자동으로 할당되었다. </code></pre>
</li>
</ul>
<p>만약 초기화 단계를 거치지 않는다면 메모리 공간에는 이전에 사용했던 값이 남아있을 수 있는데, 이 값을 쓰레기 값이라고 한다. 값을 할당하지 않은 상태에서 곧바로 변수 값을 참조하려면 쓰레기 값이 나올 수 있기 때문에 초기화가 필요하다. </p>
<h4 id="var-키워드-사용-시">var 키워드 사용 시</h4>
<p>선언 단계와 초기화 단계가 동시에 진행된다. 
<img src="https://velog.velcdn.com/images/florence_y/post/3e467b70-8ec5-4641-b1c2-1e5c569cd152/image.svg" alt=""></p>
<h4 id="let-키워드-사용-시">let 키워드 사용 시</h4>
<p>선언 단계와 초기화 단계가 분리되어 진행된다. 런타임 이전에 자바스크립트 엔진에 의해 선언단계가 먼저 실행되고, 변수 선언문에 도달했을 때 초기화 단계가 실행된다. 
만약 초기화 단계가 실행되기 전에 변수에 접근하려 하면 ReferenceError가 발생한다. </p>
<pre><code class="language-javascript">// 런타임 이전에 선언 단계 실행

console.log(user); // ReferenceError: user is not defined

let user; // 변수 선언문에서 초기화 단계 실행
console.log(user);

user = 1; // 할당문에서 할당 단계 실행
console.log(user); // 1</code></pre>
<p>let 키워드로 선언한 변수는 스코프의 시작 지점부터 변수 선언문 지점까지 변수를 참조할 수 없다. 변수를 참조할 수 없는 구간을 <strong>일시적 사각지대</strong>(TDZ, Temporal Dead Zone)이라 부른다. </p>
<p><img src="https://velog.velcdn.com/images/florence_y/post/a46abef8-07ce-41e9-92c4-9b7ba835543d/image.svg" alt=""></p>
<h4 id="const-키워드-사용-시">const 키워드 사용 시</h4>
<p>const 키워드는 선언+초기화+할당 단계가 동시에 일어난다. 따라서 반드시 선언과 동시에 할당이 이루어져야 한다.</p>
<pre><code class="language-javascript">const customer; // SyntaxError: Missing initializer in const declaration</code></pre>
<h2 id="변수-호이스팅">변수 호이스팅</h2>
<p>변수 호이스팅이란, 변수 선언 위치와 상관없이 코드의 최상단으로 끌어 올려진 것처럼 동작하는 현상을 말한다. </p>
<h4 id="var-키워드-사용-시-1">var 키워드 사용 시</h4>
<p>변수 선언이 한 줄씩 순차적으로 실행되는 것이 아닌, 런타임 이전 단계에서 먼저 실행되는 변수 호이스팅이 발생한다.</p>
<pre><code class="language-javascript">console.log(member); //undefined
var member; // 변수 선언문</code></pre>
<p>→ 코드가 순차적으로 실행된다면 <code>console.log(member);</code>가 실행될 때 ReferenceError가 발생해야 하지만, undefined가 출력된다. 런타임 이전 단계에서 먼저 실행되기 때문이다. </p>
<h4 id="let-키워드-사용-시-1">let 키워드 사용 시</h4>
<p>let 키워드로 선언한 변수는 일시적 사각지대에서는 변수를 참조할 수 없기 때문에 변수 호이스팅이 발생하지 않는 것처럼 보이지만, 사실 호이스팅이 발생한다.</p>
<pre><code class="language-javascript">let user = 1; // 전역 변수

{
    console.log(user); // ReferenceError: Cannot access &#39;user&#39; before initialization
      let user = 2; // 지역 변수
}</code></pre>
<p>→ 만약 호이스팅이 발생하지 않는다면, 코드가 순차적으로 실행되어 전역변수 user의 값을 출력해야 한다. 하지만 호이스팅이 일어나기 때문에 ReferenceError가 발생하는 것이다. </p>
<h4 id="const-키워드-사용-시-1">const 키워드 사용 시</h4>
<p>const 키워드로 선언한 변수는 let 키워드로 선언한 변수처럼 변수 호이스팅이 발생하지 않는 것처럼 동작하지만, 실제로 호이스팅이 발생한다. </p>
<pre><code class="language-javascript">const customer = 1; // 전역변수

{
    console.log(customer); // ReferenceError: Cannot access &#39;customer&#39; before initialization
      const customer = 2; // 지역변수
}</code></pre>
<h2 id="스코프">스코프</h2>
<p>스코프는 식별자(변수 이름, 함수 이름, 클래스 이름 등)가 유효한 범위를 말한다. 코드의 가장 바깥 영역인 전역에서 선언된 변수는 전역 스코프를 갖는 전역 변수이고, 함수 몸체 내부인 지역에서 선언된 변수는 지역 스코프를 갖는 지역 변수이다. 
모든 코드 블록이 지역 스포크를 만드는 특성을 <strong>블록 레벨 스코프</strong>라 하고, 함수의 코드 블록만을 지역 스코프로 인정하는 특성을 <strong>함수 레벨 스코프</strong>라고 한다.</p>
<h4 id="var-키워드-사용-시-2">var 키워드 사용 시</h4>
<p>var 키워드로 선언한 변수는 <strong>함수 레벨 스코프</strong>를 따른다. 따라서 함수 외부에서 var 키워드로 선언한 변수는 코드 블록 내에서 선언해도 모두 전역변수가 된다. 블록 밖에서 접근이 가능하다. </p>
<pre><code class="language-javascript">var x = 10; 

if(true) {
    var x = 100; // 전역 변수. x변수 중복 선언됨
}

console.log(x); // 100</code></pre>
<h4 id="let-키워드-사용-시-2">let 키워드 사용 시</h4>
<p>let 키워드로 선언한 변수는 <strong>블록 레벨 스코프</strong>를 따른다. 따라서 블록 내에서만 접근이 가능하다. </p>
<pre><code class="language-javascript">let x = 10; // 전역 변수

if(true) {
    let x = 100; // 지역 변수
}

console.log(x); // 10</code></pre>
<h4 id="const-키워드-사용-시-2">const 키워드 사용 시</h4>
<p>const 키워드로 선언한 변수는 let 키워드로 선언한 변수와 마찬가지로 <strong>블록 레벨 스코프</strong>를 따른다. </p>
<pre><code class="language-javascript">{
    const x = 100;
      console.log(x); // 100
}

console.log(x); // ReferenceError: x is not defined</code></pre>
<h2 id="재할당">재할당</h2>
<p>재할당이란 이미 값이 할당되어 있는 변수에 새로운 값을 또다시 할당하는 것을 말한다. </p>
<h4 id="var-키워드-사용-시-3">var 키워드 사용 시</h4>
<p>var 키워드로 선언한 변수는 값을 재할당할 수 있다. </p>
<pre><code class="language-javascript">var member = &#39;kim&#39;;
member = &#39;park&#39;; </code></pre>
<p>var 키워드로 선언한 변수는 선언과 동시에 undefined로 초기화되기 때문에 엄밀히 말하면 변수에 처음으로 할당하는 것도 사실은 재할당이다. </p>
<p><img src="https://velog.velcdn.com/images/florence_y/post/defc5c40-f37c-40ae-9e8d-718cf802d654/image.svg" alt=""></p>
<p>→ 이전 값이 저장되어 있던 메모리 공간을 지우고 재할당 값을 새로 저장하는 것이 아니라, 새로운 메모리 공간을 확보하고 그 메모리 공간에 새로운 값을 저장하는 것이다. </p>
<h4 id="let-키워드-사용-시-3">let 키워드 사용 시</h4>
<p>let 키워드로 선언한 변수도 값을 재할당할 수 있다. </p>
<pre><code class="language-javascript">let user = &#39;kim&#39;;
user = &#39;park&#39;; </code></pre>
<h4 id="const-키워드-사용-시-3">const 키워드 사용 시</h4>
<p>const 키워드로 선언한 변수는 재할당이 금지된다. const로 선언한 변수를 상수(constant)라고 한다. </p>
<pre><code class="language-javascript">const customer = &#39;kim&#39;;
customer = &#39;park&#39;; // TypeError: Assignment to constant variable.</code></pre>
<br>

<h2 id="정리">정리</h2>
<p><img src="https://velog.velcdn.com/images/florence_y/post/876f4147-9e4a-48f3-bb95-18f15947538c/image.svg" alt=""></p>
<br>
<br>
<br>

<p>*<em>참고 *</em></p>
<p>모던 자바스크립트 Deep Dive (도서)</p>
<p><a href="https://ko.javascript.info/variables">https://ko.javascript.info/variables</a></p>
<br>]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] JavaScript_변수]]></title>
            <link>https://velog.io/@florence_y/TIL-JavaScript%EB%B3%80%EC%88%98</link>
            <guid>https://velog.io/@florence_y/TIL-JavaScript%EB%B3%80%EC%88%98</guid>
            <pubDate>Wed, 31 May 2023 10:08:25 GMT</pubDate>
            <description><![CDATA[<h2 id="🚀-자바스크립트-복습하기">🚀 자바스크립트 복습하기</h2>
<p>React, TypeScript 등 자바스크립트 기반의 무언가를 배울 때마다 기본기의 중요성을 느끼곤 한다. 자바스크립트를 처음 학습했을 때 어려움을 겪기도 했고, 제대로 설명할 수 있을까 생각해 보았을 때 자신이 없으므로 자바스크립트의 주요 핵심들을 다시 한번 되짚어보려 한다. </p>
<h2 id="변수-variable">변수 (Variable)</h2>
<p>변수는 하나의 값을 저장하기 위해 확보한 메모리 공간 또는 그 메모리 공간을 식별하기 위해 붙인 이름을 뜻한다. </p>
<p>자바스크립트에서는 <code>let</code> 키워드를 사용하여 변수를 선언한다. (ES6 이전에는 var 키워드 사용)</p>
<p><img src="https://velog.velcdn.com/images/florence_y/post/b41f8c05-9cf4-4d1f-a43a-59521f2dd6f5/image.svg" alt=""></p>
<ul>
<li>변수에 값을 저장하는 것을 <strong>할당</strong>(assignment)이라 하고, 변수에 저장된 값을 읽어 들이는 것을 <strong>참조</strong>(reference)라 한다. </li>
</ul>
<p><img src="https://velog.velcdn.com/images/florence_y/post/8575aa6f-53bf-48f2-a6cf-5eec9de834f8/image.svg" alt=""></p>
<p>컴퓨터는 메모리 셀의 집합체인 메모리에 데이터를 저장한다. 메모리의 각 셀은 고유의 메모리 주소를 갖고, 이 메모리 주소는 메모리 공간의 위치를 나타낸다. 
변수 이름인 식별자로 메모리 주소를 통해 메모리 공간에 저장된 값에 접근할 수 있다.</p>
<br>

<h3 id="값의-할당">값의 할당</h3>
<ul>
<li>변수에 값을 할당할 때는 할당 연산자 <code>=</code>를 사용한다. </li>
</ul>
<pre><code class="language-javascript">let color; // 변수 선언
color = &#39;red&#39;; // 값 할당</code></pre>
<ul>
<li>변수 선언과 값 할당을 한 줄로 작성할 수 있다.</li>
</ul>
<pre><code class="language-javascript">let color = &#39;red&#39;;</code></pre>
<ul>
<li><p>한 줄에 여러 변수를 선언하는 것도 가능하다.</p>
<pre><code class="language-javascript">let color = &#39;red&#39;, size = 10, shape = &#39;round&#39;;</code></pre>
<p>  → 하지만 가독성이 좋지 않으므로 한 줄에는 하나의 변수를 선언하는 것이 좋다.</p>
</li>
<li><p>이미 값이 할당된 변수에 새로운 값을 재할당 할 수도 있다.</p>
</li>
</ul>
<pre><code class="language-javascript">let color = &#39;red&#39;;
color = &#39;pink&#39;;</code></pre>
<blockquote>
</blockquote>
<p>📍 let, const 키워드를 사용하여 변수를 두 번 선언하면 에러가 발생한다.
<img src="https://velog.velcdn.com/images/florence_y/post/bb60da65-c8f4-4776-9fd8-8fffb47019a4/image.png" alt="">
따라서 변수는 한 번만 선언하고, 선언한 변수를 참조할 때는 let 없이 변수명만 사용해야 한다. </p>
<br>

<h3 id="변수-네이밍-규칙">변수 네이밍 규칙</h3>
<ul>
<li>변수명은 특수문자를 제외한 문자, 숫자, 기호 <code>_</code> 과 <code>$</code>를 포함할 수 있다.</li>
<li>변수명은 숫자로 시작할 수 없다. </li>
<li>예약어는 변수명으로 사용할 수 없다.</li>
</ul>
<p>⛔️ 예약어
<code>let</code>, <code>const</code>, <code>class</code>, <code>return</code>, <code>this</code>, <code>for</code> 등
<br></p>
<p>✔️ 알파벳 이외 한글 등 모든 언어를 변수명에 사용할 수 있다. 하지만 영어를 변수명에 사용하는 것이 국제적인 관습이므로, 변수명은 영어를 사용해서 만드는 것이 좋다.</p>
<pre><code class="language-javascript">let 이름 = &#39;...&#39;;
let 名 = &#39;...&#39;;</code></pre>
<p>✔️ 자바스크립트는 대소문자를 구별한다. 아래 변수는 각각 별개의 변수이다. </p>
<pre><code class="language-javascript">let userName;
let username;</code></pre>
<p>✔️ 변수명은 의미를 명확히 표현해야 한다. 좋은 변수명은 코드의 가독성을 높인다.</p>
<pre><code class="language-javascript">let x = 16;         // 변수가 의미하는 바를 알 수 없다.
let fontSize = 16;     // fontSize 변수는 폰트 크기를 의미한다.</code></pre>
<br>

<h3 id="네이밍-컨벤션">네이밍 컨벤션</h3>
<p><strong>카멜 케이스(camelCase)</strong>
첫 단어를 제외한 각 단어의 첫 글자를 대문자로 작성하는 표기법</p>
<pre><code class="language-javascript">let userName;</code></pre>
<p><strong>스네이크 케이스(snake_case)</strong>
단어와 단어 사이 <code>_</code> 기호를 추가하는 표기법</p>
<pre><code class="language-javascript">let user_name;</code></pre>
<p><strong>파스칼 케이스(PascalCase)</strong>
각 단어의 첫 글자를 대문자로 작성하는 표기법</p>
<pre><code class="language-javascript">let UserName;</code></pre>
<p>일관성을 유지한다면 어떤 네이밍 컨벤션을 사용해도 좋지만, 자바스크립트에서는 일반적으로 변수나 함수의 이름에는 <strong>카멜 케이스</strong>를, 생성자 함수, 클래스의 이름에는 <strong>파스칼 케이스</strong>를 사용한다. </p>
<br>
<br>
<br>

<p>*<em>참고 *</em></p>
<p>모던 자바스크립트 Deep Dive (도서)</p>
<p><a href="https://ko.javascript.info/variables">https://ko.javascript.info/variables</a></p>
<br>]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 오류해결_DocumentFragment로 투명 포장하기]]></title>
            <link>https://velog.io/@florence_y/TIL-%EC%98%A4%EB%A5%98%ED%95%B4%EA%B2%B0document.createFragment%EB%A1%9C-%ED%8F%AC%EC%9E%A5%EC%A7%80-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@florence_y/TIL-%EC%98%A4%EB%A5%98%ED%95%B4%EA%B2%B0document.createFragment%EB%A1%9C-%ED%8F%AC%EC%9E%A5%EC%A7%80-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Mon, 28 Nov 2022 17:24:44 GMT</pubDate>
            <description><![CDATA[<h2 id="💥한-페이지에-중복-렌더링-발생">💥한 페이지에 중복 렌더링 발생</h2>
<p>바닐라 JS로 SPA를 구현하는 중, a태그 클릭 시 화면 이동이 아닌 같은 페이지에 element들이 중복 렌더링되는 오류가 발생하였다🤯</p>
<p><img src="https://velog.velcdn.com/images/florence_y/post/0645b01e-97ec-47d6-9caa-c9610fb39043/image.gif" alt="">
<br></p>
<h3 id="indexhtml">index.html</h3>
<p><img src="https://velog.velcdn.com/images/florence_y/post/bb50cc85-baa0-456e-8de2-fdfd3d5ae049/image.png" alt=""></p>
<p>각 페이지를 렌더링할 때, index.html의 <u>root 태그 안에 요소들이 추가</u>된다.</p>
<p><img src="https://velog.velcdn.com/images/florence_y/post/46ee5c5a-8fc8-41ea-b358-9f07edc72639/image.png" alt=""></p>
<p>👉 root 태그 아래 header, section, main, footer 가 추가된 것을 확인할 수 있다. </p>
<h3 id="routerjs">router.js</h3>
<p><img src="https://velog.velcdn.com/images/florence_y/post/a0fa1d67-7f74-44e7-8e05-e71ccbbef4a8/image.png" alt=""></p>
<p>router.js의 render부분을 살펴보자. </p>
<p><strong>document.querySelector(this.rootElementId)</strong>로 index.html의 root를 받아오고
<strong>rootElement.innerHtml = &#39;&#39;</strong>으로 root를 초기화해준 후
<strong>rootElement.appendChild(view)</strong>로 root에 view(page) 컴포넌트를 추가해 주어야 한다.</p>
<p><img src="https://velog.velcdn.com/images/florence_y/post/b367f3ce-fbff-4eea-9101-517a45d39670/image.png" alt=""></p>
<p>👉 하지만 root 초기화가 이루어지지 않고, 기존 elements 아래에 같은 element들이 중복으로 추가되었다😨</p>
<h2 id="🔎원인">🔎원인</h2>
<p>view 컴포넌트를 확인해 보았다.</p>
<h3 id="return-값-미설정">return 값 미설정</h3>
<p><img src="https://velog.velcdn.com/images/florence_y/post/6e737c7c-f61d-4963-870f-c58c47b2ecb7/image.png" alt=""></p>
<p>Component를 불러올 때, Component의 render부분이 root에 추가되어야 하는데 <u>render의 return 값을 설정하지 않았다.</u>
render 안의 컴포넌트들을 <u>div 등으로 감싸서 return</u> 해주어야 하는데 header, footer을 포함한 html 요소들을 감싸기 위해 div를 또 생성하는 것이 괜히 불편했다.</p>
<p>나는 root 태그 안에 직접 요소들을 추가해주기 위해 타겟 요소로 root를 불러온 후 append 해주기만 했다. 그러니 라우터의 render가 이루어지지 않고, root에 계속 중복으로 append만 되었던 것....😵</p>
<h2 id="💡해결-방법">💡해결 방법</h2>
<h3 id="documentfragment">DocumentFragment</h3>
<p>멘토님께 div 하위에 heaer, footer을 추가하는 것이 불편했다 말씀드리니 <strong>DocumentFragment</strong>를 알려주셨다. </p>
<blockquote>
<p>DocumentFragment는 특별한 DOM 노드 타입으로, 여러 노드로 구성된 그룹을 감싸 다른 곳으로 전달하게 해주는 wrapper처럼 동작힙니다.<br>
문서에 있는 다른 노드를 DocumentFragment에 추가할 수 있는데, DocumentFragment를 문서 어딘가에 삽입하면 DocumentFragment는 사라지고 DocumentFragment에 추가한 노드만 남습니다.</p>
</blockquote>
<p><strong>DocumentFragment</strong>는 메모리에서만 정의되는 경량화된 문서 객체이다. <u>DOM에 추가하면 DocumentFragment node는 사라지고 자식 node만 추가</u>된다. 한마디로 투명 포장지같은 존재..</p>
<p>이 <strong>DocumentFragment</strong>라는 투명 포장지에 element들을 담아보자.</p>
<p><img src="https://velog.velcdn.com/images/florence_y/post/ee23a739-87c3-4c83-b076-e3a7236e8718/image.png" alt=""></p>
<p>root를 불러오는 타겟 요소를 지워주고
<code>document.createDocumentFragment()</code>로 DocumentFragment를 생성한 후 요소들을 append 해주었다.
마지막으로 DocumentFragment를 return 해주면..
<img src="https://velog.velcdn.com/images/florence_y/post/8a9d4270-5d89-40f5-a374-d9ef0590bfca/image.png" alt="">
👉 이렇게 root 하위로 DocumentFragment에 담아둔 element들이 추가되었다. 투명 포장지는 보이지 않는다!
<br></p>
<h2 id="✔️해결-완료">✔️해결 완료</h2>
<p>화면도 잘 이동하고, 렌더링도 잘 실행된다.
<img src="https://velog.velcdn.com/images/florence_y/post/1d8ec967-bbfb-4fe2-95b7-6b9eae679c39/image.gif" alt=""></p>
<p><img src="https://velog.velcdn.com/images/florence_y/post/ed42e31e-49c0-47ad-ad8c-08154e5ad4f0/image.gif" alt=""></p>
<br>
<br>
<br>


<p><strong>참고 사이트</strong></p>
<p><a href="https://ko.javascript.info/modifying-document#document-fragment">https://ko.javascript.info/modifying-document#document-fragment</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] console.log만 있는 게 아니고]]></title>
            <link>https://velog.io/@florence_y/TIL-console.log%EB%A7%8C-%EC%9E%88%EB%8A%94-%EA%B2%8C-%EC%95%84%EB%8B%88%EA%B3%A0</link>
            <guid>https://velog.io/@florence_y/TIL-console.log%EB%A7%8C-%EC%9E%88%EB%8A%94-%EA%B2%8C-%EC%95%84%EB%8B%88%EA%B3%A0</guid>
            <pubDate>Thu, 15 Sep 2022 09:14:58 GMT</pubDate>
            <description><![CDATA[<h1 id="🔧-console">🔧 Console</h1>
<p>자바스크립트 개발을 할 때 자주 사용하는 것이 <code>console.log()</code>가 아닐까 싶다. <code>console.log()</code>로 ()안의 값을 콘솔창에 출력하는데, 출력된 값을 확인하여 디버깅(<em>오류를 찾고 수정</em> )하는 데 주로 사용된다. 
console에는 우리가 자주 사용하는 <code>console.log()</code>를 포함한 다양한 메서드들이 있다. 알아두면 유용하게 쓰일 console의 주요 매서드들에 대해 알아보자. </p>
<br>

<h2 id="consolelog">console.log()</h2>
<p>log 메서드는 콘솔에 메세지를 출력하게 한다. 메세지는 문자열, 숫자, 객체 등이 될 수 있다.</p>
<img src="https://velog.velcdn.com/images/florence_y/post/f3cde89c-21ff-41f6-8555-90b05e029861/image.png" width="350">


<p>🔎 <strong><code>console.log()</code>를 출력했을 때 undefined가 나오는 이유</strong>
<code>console.log()</code>는 return 값이 없다. 특정 return 값을 반환하는 것이 아닌 입력값을 출력하는 역할을 하기 때문이다.</p>
<h3 id="s">%s</h3>
<p><code>%s</code>를 사용하여 문자열을 치환할 수 있다. </p>
<img src="https://velog.velcdn.com/images/florence_y/post/3a6db867-59af-4c30-a683-5d09e29017cc/image.png" width=350>

<p>📌 하지만 ES6부터 주로 백틱(｀)을 사용하여 문자열을 치환한다.</p>
<img src="https://velog.velcdn.com/images/florence_y/post/734ca85d-a398-4abf-809b-9eeba4c8523d/image.png" width=350>

<h3 id="c">%c</h3>
<p><code>%c</code>를 사용하여 스타일을 지정하여 출력할 수 있다.</p>
<img src="https://velog.velcdn.com/images/florence_y/post/8f7cad80-0a2b-4270-a680-a69d78b211c3/image.png" width=500>

<br>

<h2 id="consolewarn">console.warn()</h2>
<p><code>console.warn()</code>은 콘솔창에 경고 메세지를 출력한다.</p>
<img src="https://velog.velcdn.com/images/florence_y/post/bbecc5df-c556-4702-933d-b04d511e1cb7/image.png" width="350px">

<br>

<h2 id="consoleerror">console.error()</h2>
<p><code>console.error()</code>은 콘솔창에 에러 메세지를 출력한다.</p>
<img src="https://velog.velcdn.com/images/florence_y/post/b5e5f4bc-62c5-44da-8ed4-f5bb51c449c8/image.png" width="350px">

<br>

<h2 id="consoleinfo">console.info()</h2>
<p><code>console.info()</code>는 콘솔창에 정보 메세지를 출력한다.</p>
<img src="https://velog.velcdn.com/images/florence_y/post/47de0ea8-2d0b-4d05-91f9-fc8411348b81/image.png" width="380px">

<br>

<h2 id="consoleassert">console.assert()</h2>
<p><code>console.assert()</code>는 주어진 조건이 false일 때만 에러 메세지를 출력한다. </p>
<img src="https://velog.velcdn.com/images/florence_y/post/8c4e8a90-180b-4850-85f7-21e88be80997/image.png" width="400px">

<br>

<h2 id="consoleclear">console.clear()</h2>
<p><code>console.clear()</code>은 콘솔에 출력된 내용을 지운다. </p>
<img src="https://velog.velcdn.com/images/florence_y/post/0430a454-df0a-4434-8d0e-eef1f62cee5c/image.png" width="350px">

<p>📌 <strong>콘솔창 지우기 단축키</strong></p>
<p><code>Ctrl + L</code> (windows)
<code>Command + K</code> (macOS)</p>
<br>

<h2 id="consoledir">console.dir()</h2>
<p><code>console.dir()</code>은 자바스크립트 객체의 속성들을 출력한다. </p>
<pre><code>&lt;body&gt;

&lt;p onClick=&quot;makePink()&quot;&gt;Click Me&lt;/p&gt;

  &lt;script&gt;

    function makePink() {
      const p = document.querySelector(&#39;p&#39;);
      p.style.color = &#39;#F8BBD0&#39;;
      p.style.fontSize = &#39;50px&#39;;
    }

  &lt;/script&gt;

  &lt;/body&gt;</code></pre><p>📍 <strong><code>console.log()</code>와 <code>console.dir()</code>의 차이점</strong></p>
<p>DOM 객체에서 <code>console.log()</code>는 HTML 태그 자체를 출력하지만</p>
<img src="https://velog.velcdn.com/images/florence_y/post/dcf5f1d5-f1f1-4226-bd01-e7f8d3fbd362/image.png" width="400px">

<p><code>console.dir()</code>은 객체가 가지고 있는 모든 속성을 출력한다.</p>
<img src="https://velog.velcdn.com/images/florence_y/post/6a547b1f-bd5a-43b8-a593-8dc86833a854/image.png" width="350px">

<br>

<h2 id="consolegroup">console.group()</h2>
<p><code>console.group()</code>은 콘솔에 그룹을 생성하여 출력한다.
<code>console.groupEnd()</code>로 그룹을 종료한다.</p>
<img src="https://velog.velcdn.com/images/florence_y/post/02827639-e4b6-4a34-8710-95b887e6ef62/image.png" width="350px">

<br>

<h2 id="consolecount">console.count()</h2>
<p><code>console.count()</code>은 몇 번 호출되었는지 카운트한다.</p>
<img src="https://velog.velcdn.com/images/florence_y/post/06cc230b-55f1-43c1-819a-9728e964f6fa/image.png" width="350px">

<br>

<h2 id="consoletime">console.time()</h2>
<p><code>console.time()</code>은 작업이 얼마나 걸리는지 측정한다. 
<code>console.timeEnd()</code>로 종료하고, 콘솔창에 측정한 시간을 출력한다.</p>
<img src="https://velog.velcdn.com/images/florence_y/post/f73b2e6c-4d0d-4493-94b5-23a9e611e2a5/image.png" width="350px">

<p><code>console.time()</code>과 <code>console.timeEnd()</code>에 동일한 label을 지정해야 한다.</p>
<br>

<h2 id="consoletable">console.table()</h2>
<p><code>console.time()</code>은 데이터를 테이블 형식으로 출력한다. </p>
<p><img src="https://velog.velcdn.com/images/florence_y/post/2e99b254-a2b3-4078-9eeb-f6024095c740/image.png" alt=""></p>
<br>
<br>
<br>

<p><strong>참고 사이트</strong></p>
<p><a href="https://courses.wesbos.com/account/access/630ef22c88db94aff320bc95/view/194129876">https://courses.wesbos.com/account/access/630ef22c88db94aff320bc95/view/194129876</a></p>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/API/console">https://developer.mozilla.org/en-US/docs/Web/API/console</a></p>
<br>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 생성자 함수에서의 프로토타입, 상속]]></title>
            <link>https://velog.io/@florence_y/TIL-%EC%83%9D%EC%84%B1%EC%9E%90-%ED%95%A8%EC%88%98%EC%97%90%EC%84%9C%EC%9D%98-%ED%94%84%EB%A1%9C%ED%86%A0%ED%83%80%EC%9E%85-%EC%83%81%EC%86%8D</link>
            <guid>https://velog.io/@florence_y/TIL-%EC%83%9D%EC%84%B1%EC%9E%90-%ED%95%A8%EC%88%98%EC%97%90%EC%84%9C%EC%9D%98-%ED%94%84%EB%A1%9C%ED%86%A0%ED%83%80%EC%9E%85-%EC%83%81%EC%86%8D</guid>
            <pubDate>Sun, 22 May 2022 05:20:34 GMT</pubDate>
            <description><![CDATA[<h1 id="today-i-learned📚">Today I Learned📚</h1>
<h2 id="prototype">Prototype</h2>
<p>자바스크립트는 프로토타입 기반의 객체지향 프로그래밍 언어이다.</p>
<p>C++나 자바 같은 클래스 기반 객체지향 프로그래밍 언어는 클래스가 있고, 클래스를 통해 상속을 사용한다. 하지만 자바스크립트는 클래스가 없고 객체를 프로토타입 기반으로 복제를 통해 생성한다. (자바스크립트의 class 는 ES6에서 추가된 문법) </p>
<p>프로토타입은 상속하고자 하는 속성과 메소드를 담아두는 공간(유전자). 모든 객체는 프로토타입으로부터 프로퍼티와 메소드를 상속받는다.</p>
<p>클래스는 붕어빵 틀. 클래스 기반에서 객체는 붕어빵 틀인 클래스로 붕어빵을 찍어내지만, 프로토타입 기반에서 객체는 유전자인 프로토타입을 복제해서 사용한다.</p>
<pre><code class="language-javascript">function Parent() { 
    this.health = &#39;good&#39;;
    this.skin = &#39;dry&#39;;
}

const child = new Parent ()

Parent.prototype.hair = &#39;thick&#39; // prototype에 추가 가능

child.hair  

// thick</code></pre>
<p>→ Parent의 prototype에 hair라는 데이터를 추가하면, child에서도 Parent의 데이터를 끌어올 수 있다. </p>
<h2 id="프로토타입-체인">프로토타입 체인</h2>
<p>객체의 프로퍼티에 접근하려고 할 때 해당 객체에 접근하려는 프로퍼티가 없으면 부모 프로토타입의 프로퍼티를 순차적으로 검색한다. </p>
<p>✔ <strong>proto</strong> : 부모에게 유전받은 프로토타입을 참조하는 속성</p>
<h2 id="생성자-함수에서의-프로토타입">생성자 함수에서의 프로토타입</h2>
<p>생성자 함수를 통해 쉽게 객체를 생성할 수 있지만, 객체의 메서드를 등록할 때마다 새로운 함수가 생성된다.</p>
<pre><code class="language-javascript">function Member(name) {
    this.name = name;
    this.sayHello = function() {
        console.log(`Good morning, ${this.name}!`); 
    } // 객체의 메서드 등록 할 때마다 새로운 함수 생성
}  

const mem1 = new Member(&#39;민영&#39;);
const mem2=  new Member(&#39;지훈&#39;);</code></pre>
<p>여기서 문제는 객체의 메서드를 등록할 때마다 sayHello이라는 새로운 함수를 생성한다. 동일한 생성자 함수에 의해 생성된 모든 인스턴스가 동일한 메서드를 중복 소유하는 것은 메모리를 낭비하는 것이다. </p>
<p><img src="https://velog.velcdn.com/images/florence_y/post/9f51e3b0-d282-44e4-ad4c-4ff6a886abda/image.png" alt=""></p>
<p>이 문제를 해결하기 위해 프로토타입을 사용한다. Member 생성자 함수가 생성한 모든 인스턴스가 sayHello 메서드를 공유해서 사용할 수 있도록 프로토타입에 추가한다. </p>
<pre><code class="language-javascript">function Member(name) {
    this.name = name;
}  

Member.prototype.sayHello = function() { 
        console.log(`Good morning, ${this.name}!`); 
}

const mem1 = new Member(&#39;민영&#39;);
const mem2=  new Member(&#39;지훈&#39;);</code></pre>
<p>프로토타입은 Member 생성자 함수의  prototype 프로퍼티에 바인딩되어 있다. Member 생성자 함수가 생성한 모든 인스턴스는 Member.prototype으로부터 하나의 sayHello메서드를 공유한다. 
<img src="https://velog.velcdn.com/images/florence_y/post/c01cb733-2202-4c01-ae4e-d4bd7d712abb/image.png" alt=""></p>
<p>sayHello 메서드는 단 하나만 생성되어 프로토타입인 Memeber.prototype의 메서드로 할당되어 있다. 따라서 Member 생성자 함수가 생성하는 모든 인스턴스는 sayHello메서드를 상속받아 사용할 수 있다.  </p>
<h2 id="objectcreate에-의한-상속">Object.create에 의한 상속</h2>
<p>부모 역할을 할 생성자 함수</p>
<pre><code class="language-javascript">function Parent() { 
    this.health = &#39;good&#39;;
    this.skin = &#39;dry&#39;;
}

Parent.prototype.hair = &#39;thick&#39;
</code></pre>
<p>자식 역할을 할 생성자 함수</p>
<pre><code class="language-javascript">function Child() {
    Parent.call(this);
}  // call 함수는 Child함수의 this가 Parent생성자 함수의 this를 바라보게 한다.

Child.prototype = Object.create(Parent.prototype);
// 인자를 Child.prototype에 연결한다.

const junior = new Child()</code></pre>
<p>Object.create는 프로토타입을 지정해서 새로운 객체를 생성한다.  Object.create의 첫 번째 매개변수는 프로토타입을 지정할 객체(부모)를 전달한다. 두 번째 매개변수는 생성할 객체의 프로퍼티를 전달한다. Parent의 프로토타입을 Child의 프로토타입이 참조하게 하라는 것이다. </p>
]]></description>
        </item>
    </channel>
</rss>