<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>yoonsuk_choi.log</title>
        <link>https://velog.io/</link>
        <description>Name : 최윤석(YoonSuk Choi)</description>
        <lastBuildDate>Wed, 08 Jan 2025 14:51:11 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>yoonsuk_choi.log</title>
            <url>https://velog.velcdn.com/images/yoonsuk_choi/profile/a8241e8a-f693-4cde-8f31-8beb8f52425d/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. yoonsuk_choi.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/yoonsuk_choi" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[project[Puleus]_(3주차) 코딩과 디버깅 그리고 크리스마스]]></title>
            <link>https://velog.io/@yoonsuk_choi/projectPuleus3%EC%A3%BC%EC%B0%A8-%EC%BD%94%EB%94%A9%EA%B3%BC-%EB%94%94%EB%B2%84%EA%B9%85-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%ED%81%AC%EB%A6%AC%EC%8A%A4%EB%A7%88%EC%8A%A4</link>
            <guid>https://velog.io/@yoonsuk_choi/projectPuleus3%EC%A3%BC%EC%B0%A8-%EC%BD%94%EB%94%A9%EA%B3%BC-%EB%94%94%EB%B2%84%EA%B9%85-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%ED%81%AC%EB%A6%AC%EC%8A%A4%EB%A7%88%EC%8A%A4</guid>
            <pubDate>Wed, 08 Jan 2025 14:51:11 GMT</pubDate>
            <description><![CDATA[<p>기간 : 24.12.23~ 24.12.29<img src="https://velog.velcdn.com/images/yoonsuk_choi/post/19d35e92-81ca-4a7d-8203-cc0b60e797ae/image.JPG" alt=""></p>
<h2 id="🌱-코딩과-디버깅-그리고-크리스마스">🌱 코딩과 디버깅 그리고 크리스마스</h2>
<ul>
<li>프로젝트가 7일 남았다. 하지만 25일 크리스마스로 인해 실질적인 미팅과 논의를 할 수 있는 시간은 2일에 불과했다. 또한 23일 임시 배포를 해야하기 때문에 바쁜 3주차였다. </li>
</ul>
<hr>
<h2 id="💡-meeting">💡 Meeting</h2>
<ul>
<li>팀원들과 각자 담당 분야의 진행 상황을 공유했다.</li>
<li>Git의 충돌을 방지하는 것이 다시 한번 언급 되었다.<ul>
<li>반드시 자신의 branch를 설정한 뒤 작업을 진행 한다. </li>
<li>branch에서 충돌이 없음을 확인한 뒤 수정 사항을 커밋 메세지와 함께 업로드한다.<h2 id="✨-개인-작업">✨ 개인 작업</h2>
<h3 id="💻-자유-게시판-기능">💻 자유 게시판 기능</h3>
</li>
</ul>
</li>
<li>자유게시판은 사용자들이 게시글을 작성, 조회, 수정, 삭제할 수 있는 기능을 가지고 있다.</li>
<li>사용자들은 자유게시판을 통해 정보, 의견을 교류하여 우리 페이지를 오랫동안 사용 할 수 있게 유도한다.<h4 id="게시글-목록">게시글 목록</h4>
</li>
<li><strong>기능</strong>: 모든 게시글을 테이블 형태로 조회.</li>
<li><strong>구성 요소</strong>:<ul>
<li>제목, 작성일, 작성자, 분류 정보 표시.</li>
<li>각 게시글의 제목을 클릭하면 상세 페이지로 이동.</li>
</ul>
</li>
<li><strong>조건</strong>:<ul>
<li>게시글이 없는 경우 &quot;게시글이 없습니다.&quot; 메시지 표시.</li>
</ul>
</li>
</ul>
<h4 id="게시글-작성">게시글 작성</h4>
<ul>
<li><strong>기능</strong>: 로그인한 사용자만 게시글 작성 가능.</li>
<li><strong>구성 요소</strong>:<ul>
<li>게시글 제목, 내용, 분류, 이미지 업로드 가능.</li>
<li>작성 버튼 클릭 시 서버로 데이터 전송.</li>
</ul>
</li>
</ul>
<h4 id="게시글-조회">게시글 조회</h4>
<ul>
<li><strong>기능</strong>: 특정 게시글의 상세 내용을 조회.</li>
<li><strong>구성 요소</strong>:<ul>
<li>제목, 작성일, 작성자, 내용, 분류 정보 표시.</li>
<li>댓글 작성 및 조회 가능.</li>
</ul>
</li>
</ul>
<h4 id="댓글-기능">댓글 기능</h4>
<ul>
<li><strong>기능</strong>: 게시글에 댓글 추가, 수정, 삭제.</li>
<li><strong>구성 요소</strong>:<ul>
<li>로그인 상태에서 댓글 작성 가능.</li>
<li>댓글 수정 및 삭제는 작성자만 가능.</li>
</ul>
</li>
</ul>
<h4 id="게시글-관리">게시글 관리</h4>
<ul>
<li><strong>기능</strong>: 게시글 수정 및 삭제.</li>
<li><strong>구성 요소</strong>:<ul>
<li>게시글 작성자는 자신의 게시글 수정 및 삭제 가능.</li>
</ul>
</li>
</ul>
<h3 id="💻-자유-게시판-기술">💻 자유 게시판 기술</h3>
<h4 id="5-라우트-정의-indexjs">5. 라우트 정의 (<code>index.js</code>)</h4>
<ul>
<li><strong>라우팅</strong></li>
</ul>
<table>
<thead>
<tr>
<th>라우팅</th>
<th>내용</th>
</tr>
</thead>
<tbody><tr>
<td>GET /board</td>
<td>게시판 목록 표시</td>
</tr>
<tr>
<td>GET /board/write</td>
<td>게시글 작성 폼 표시</td>
</tr>
<tr>
<td>POST /board/write</td>
<td>게시글 데이터 업로드</td>
</tr>
<tr>
<td>GET /board/view/:id</td>
<td>특정 게시글 조회</td>
</tr>
<tr>
<td>POST /board/view/:id/comment</td>
<td>댓글 작성</td>
</tr>
<tr>
<td>PUT /board/comment/:id/edit</td>
<td>댓글 수정</td>
</tr>
<tr>
<td>DELETE /board/comment/:id</td>
<td>댓글 삭제</td>
</tr>
<tr>
<td>DELETE /board/view/:id/delete</td>
<td>게시글 삭제</td>
</tr>
</tbody></table>
<ul>
<li><strong>미들웨어</strong><ul>
<li><code>isAuthenticated</code>: 로그인 여부 확인.</li>
<li><code>setUserId</code>: 로그인 상태의 사용자 ID를 로컬 변수에 저장.</li>
</ul>
</li>
</ul>
<h4 id="2-데이터베이스-모델-boardjs">2. 데이터베이스 모델 (<code>board.js</code>)</h4>
<ul>
<li><strong>구조</strong>: <code>board</code> 테이블</li>
<li><strong>필드</strong></li>
</ul>
<table>
<thead>
<tr>
<th>라우팅</th>
<th>세부</th>
<th>내용</th>
<th>검사</th>
</tr>
</thead>
<tbody><tr>
<td>boardId</td>
<td>Primary Key, Auto Increment</td>
<td>게시글 고유 식별자</td>
<td>-</td>
</tr>
<tr>
<td>boardTitle</td>
<td>String, 50</td>
<td>게시글 제목</td>
<td>필수</td>
</tr>
<tr>
<td>boardDate</td>
<td>Date</td>
<td>게시일(현재 시간)</td>
<td>기본값</td>
</tr>
<tr>
<td>boardDetail</td>
<td>String, 300</td>
<td>게시글 내용</td>
<td>필수</td>
</tr>
<tr>
<td>boardPicPath</td>
<td>String, 100</td>
<td>첨부 이미지</td>
<td>선택</td>
</tr>
<tr>
<td>userId</td>
<td>String, 20</td>
<td>작성자 ID</td>
<td>선택</td>
</tr>
<tr>
<td>boardCategory</td>
<td>String, 20</td>
<td>게시글 분류</td>
<td>필수</td>
</tr>
</tbody></table>
<ul>
<li><strong>설정</strong>:<ul>
<li><code>freezeTableName</code>: 테이블 이름 고정.</li>
<li><code>timestamps</code>: 생성 및 업데이트 시간 자동 기록 비활성화.</li>
</ul>
</li>
</ul>
<h4 id="3-html-구조-boardejs">3. HTML 구조 (<code>board.ejs</code>)</h4>
<ul>
<li><strong>기능</strong>:<ul>
<li>게시글 목록을 표시하며, 조건부 렌더링으로 게시글 유무에 따라 동적 처리.</li>
<li>로그인 여부에 따라 게시글 작성 버튼 또는 로그인 안내 표시.</li>
</ul>
</li>
<li><strong>구성</strong>:<ul>
<li><code>&lt;table&gt;</code>: 게시글 목록 테이블.</li>
<li><code>&lt;div class=&quot;write-btn&quot;&gt;</code>: 작성 버튼 또는 로그인 안내.</li>
</ul>
</li>
<li><strong>템플릿 엔진</strong>: <code>&lt;% %&gt;</code>를 사용한 동적 렌더링.</li>
</ul>
<h4 id="4-css-스타일링-boardcss">4. CSS 스타일링 (<code>board.css</code>)</h4>
<ul>
<li><strong>기능</strong>:<ul>
<li>레이아웃 및 디자인 제공.</li>
<li>주요 스타일:<ul>
<li><code>body</code>: 기본 배경과 글꼴 설정.</li>
<li><code>.board-list</code>: 게시판 레이아웃 정의.</li>
<li><code>.btn-red</code>: 버튼 스타일 정의.</li>
</ul>
</li>
</ul>
</li>
<li><strong>사용성</strong>:<ul>
<li>테이블, 버튼, 폼 등 모든 요소에 반응형 디자인 적용.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="📘-meeting">📘 Meeting</h2>
<h3 id="트러블-슈팅">트러블 슈팅</h3>
<h4 id="🔥안건">🔥안건</h4>
<ul>
<li>&quot;상품 정보&quot;가 없어서 현재 구현 정도의 한계점이 발생</li>
<li>ERD기반 연결이 되지 앟아 페이지 오류</li>
<li>SQL 테이블명 확인하면서 코딩<h3 id="🔎-해결방안">🔎 해결방안</h3>
<h4 id="1-erd-sql을-기준으로-코딩">1. ERD, SQL을 기준으로 코딩</h4>
<img src="https://velog.velcdn.com/images/yoonsuk_choi/post/6f557c95-1430-4d2b-ad80-c4350fa50af2/image.png" alt=""><h4 id="2-상품-정보-크롤링이-오래-걸릴-경우-실시간-크롤링-방식을-이미지-가져오기-방식으로-변경하기로-결정했다">2. 상품 정보 크롤링이 오래 걸릴 경우 실시간 크롤링 방식을 이미지 가져오기 방식으로 변경하기로 결정했다.</h4>
</li>
</ul>
<hr>
<h2 id="⚙️-기술적-보완점">⚙️ 기술적 보완점</h2>
<ol>
<li>배포하기<ul>
<li>AWS 배포 과정에서 원인 불명의 오류가 계속 발생. </li>
<li>Leader의 도움을 받아야함.</li>
</ul>
</li>
<li>게시글<ul>
<li>게시글 목록 페이징 처리.</li>
</ul>
</li>
<li>DB 쿼리<ul>
<li>DB 쿼리에 인덱스 추가.</li>
</ul>
</li>
<li>UI/UX 개선<ul>
<li>모바일 사용자를 위한 반응형 디자인 추가.</li>
<li>게시글 검색 및 필터링 기능.</li>
</ul>
</li>
<li>보안 강화<ul>
<li>현재 로그인을 하면 입력 값에 대한 보안사항 부재.</li>
<li>입력 값에 대한 유효성 검사 추가해 &quot;글 내용&quot; 충실하게 개선</li>
</ul>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[project[Puleus]_(2주차)편의점 제품 리뷰 웹 페이지]]></title>
            <link>https://velog.io/@yoonsuk_choi/projectPuleus-2%EC%A3%BC%EC%B0%A8%ED%8E%B8%EC%9D%98%EC%A0%90-%EC%A0%9C%ED%92%88-%EB%A6%AC%EB%B7%B0-%EC%9B%B9-%ED%8E%98%EC%9D%B4%EC%A7%80</link>
            <guid>https://velog.io/@yoonsuk_choi/projectPuleus-2%EC%A3%BC%EC%B0%A8%ED%8E%B8%EC%9D%98%EC%A0%90-%EC%A0%9C%ED%92%88-%EB%A6%AC%EB%B7%B0-%EC%9B%B9-%ED%8E%98%EC%9D%B4%EC%A7%80</guid>
            <pubDate>Wed, 08 Jan 2025 05:30:47 GMT</pubDate>
            <description><![CDATA[<p>기간 : 24.12.16~ 24.12.22<img src="https://velog.velcdn.com/images/yoonsuk_choi/post/a64910fd-cc39-42c0-881e-79fdc3f95e95/image.JPG" alt=""></p>
<h2 id="🌱-편의점-제품-리뷰-웹-페이지">🌱 편의점 제품 리뷰 웹 페이지</h2>
<ul>
<li><input disabled="" type="checkbox"> 프로젝트 GitHub 공용 폴더 연결</li>
<li><input disabled="" type="checkbox"> 개인 PC에 git 폴더 연결, branch 설정</li>
<li><input disabled="" type="checkbox"> 프로젝트 세부 일정 계획<h2 id="💡-meeting">💡 Meeting</h2>
</li>
<li>본격적인 코딩을 하기 전, 세부 규칙을 정해 팀 프로젝트를 진행한다.<h2 id="✨-코딩-규칙">✨ 코딩 규칙</h2>
</li>
</ul>
<ol>
<li><strong>데이터 요청 방식 결정</strong><ul>
<li><strong>논의 내용</strong>: 프로젝트에서 <code>axios</code>와 <code>fetch</code> 중 어떤 라이브러리를 사용할지 결정.</li>
<li><code>axios</code> 사용,</li>
</ul>
</li>
<li><strong>인증 방식 결정</strong><ul>
<li><strong>논의 내용</strong>: 인증 및 세션 관리 방식을 <code>세션</code>과 <code>JWT</code> 중 선택.</li>
<li><strong>결론</strong>: <code>세션</code> 사용.</li>
</ul>
</li>
<li><strong>커밋 및 브랜치 컨벤션 설정</strong><ul>
<li><strong>논의 내용</strong>: 커밋 메시지 및 브랜치 네이밍 컨벤션.</li>
<li>개인 브런치, 날짜, 내용</li>
<li><strong>결론</strong>: 커밋 메시지 예시: <code>[backend]/FEAT: (241212) 로그인기능 추가</code><ul>
<li>커밋 메시지 예시: <code>[backend]/FEAT: (241212) 로그인기능 추가</code></li>
</ul>
</li>
</ul>
</li>
<li><strong>JSdocs 사용 여부</strong><ul>
<li><strong>논의 내용</strong>: 코드 문서화를 위해 <code>JSdocs</code>를 사용할지 여부.</li>
<li><strong>결론</strong>: 오류 발생 가능성으로 인해 사용하지 않기로 결정.</li>
</ul>
</li>
<li><strong>식별자 및 CSS 클래스 네이밍 규칙</strong><ul>
<li><strong>논의 내용</strong>: 식별자와 CSS 클래스 이름 규칙.</li>
<li><strong>결론</strong>: <strong>식별자</strong>: <code>camelCase</code> 사용. <strong>CSS 클래스 이름</strong>: 동일하게 <code>camelCase</code> 적용.</li>
</ul>
</li>
<li><strong><code>root.css</code> 사용 여부</strong><ul>
<li><strong>논의 내용</strong>: <code>root.css</code> 파일을 활용한 기본 스타일 변수 설정.</li>
<li><strong>결론</strong>: 사용하기로 결정.</li>
</ul>
</li>
<li><strong>서버 함수 관리 방식</strong><ul>
<li><strong>논의 내용</strong>: 서버에서 사용하는 함수들을 <code>Utils</code> 폴더로 관리할지, 또는 <code>controller</code>에 작성할지 여부.</li>
<li><strong>결론</strong>: <code>Utils</code> 폴더를 따로 관리하여 코드 수정에 대응 관리.</li>
</ul>
</li>
<li><strong>비밀번호 암호화 방식</strong><ul>
<li><strong>논의 내용</strong>: 암호화 방식 선택 (예: salt 사용 여부에 따른 테이블 구조 변화).</li>
<li><strong>결론</strong>: 추후 결정.</li>
</ul>
</li>
<li><strong>기획 및 일정 설정</strong><ul>
<li><strong>논의 내용</strong>: 발표일 전까지 주요 기능 및 일정 초안 마련.</li>
<li><strong>결론</strong>: 추후 공유 예정.</li>
</ul>
</li>
<li><strong>반응형 디자인</strong><ul>
<li><strong>논의 내용</strong>: 반응형 웹의 지원 디바이스의 제작 범위</li>
<li><strong>결론</strong>:지원 디바이스: <strong>모바일, 태블릿, 컴퓨터</strong>.<h3 id="💻-추후-과제">💻 추후 과제</h3>
</li>
</ul>
</li>
</ol>
<ul>
<li><strong>비밀번호 암호화 방식 최종 결정.</strong></li>
<li><strong>주요 기능 및 발표일까지의 세부 일정 공유.</strong></li>
<li><strong>반응형 디자인 적용 계획 검토 및 디테일 추가.</strong><h2 id="📘-meeting">📘 Meeting</h2>
<h3 id="트러블-슈팅">트러블 슈팅</h3>
<h4 id="🔥-프로젝트-안건">🔥 프로젝트 안건</h4>
<ol>
<li>아직 결정 되지 않은 문제에 대해서 논의를 해야하는데, 서로 가진 정보가 불투명해서 논의가 성립되지 않는다.</li>
<li>구현 아이디어는 좋으나 세부 일정이 정해지지 않았다.<h4 id="🔥-github-안건">🔥 GitHub 안건</h4>
</li>
<li>GitHub에 각자 작업물 업로드 충돌 방지</li>
<li>GitHub에 업로드 후 리뷰 요청 슬랙에 업로드<h4 id="🔎-해결방안">🔎 해결방안</h4>
</li>
</ol>
</li>
<li>전체적인 구성원 간 소통 부족이 나타났다. 필수적으로 충돌 생기지 않게 공용 폴더는 건들지 않도록 한다.</li>
<li>업무 현황 [시작, 과정, 종료] 상호 연락 작업물 GIT 프로젝트 공유 후 승인 요청은 댓글로 작성</li>
<li>구현 아이디어의 실현 방법은 Leader에게 의견을 물어 본 뒤에 결정하자.<h2 id="⚙️-다음-회의-준비">⚙️ 다음 회의 준비</h2>
편의점의 상품을 보여주는 기능이 구현 되기 위해 먼저 자료가 있어야한다. 굳이 크롤링을 해야하는가 필요성이 문제시 되었지만, 크롤링 기능을 구현하는 것도 중요한 기획이라고 결론냈다.</li>
<li>자료수집 우선적으로 작업할 것</li>
<li>코딩 후 결과물 GitHub에 업로드</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[학습 - 비밀번호 암호화 : bcrypt]]></title>
            <link>https://velog.io/@yoonsuk_choi/%ED%95%99%EC%8A%B5-%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8-%EC%95%94%ED%98%B8%ED%99%94-bcrypt</link>
            <guid>https://velog.io/@yoonsuk_choi/%ED%95%99%EC%8A%B5-%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8-%EC%95%94%ED%98%B8%ED%99%94-bcrypt</guid>
            <pubDate>Tue, 07 Jan 2025 16:54:41 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/yoonsuk_choi/post/5bbf6283-87be-4119-9632-9d49b95fffc6/image.JPG" alt=""></p>
<h2 id="암호화-bcrypt">암호화 bcrypt</h2>
<ul>
<li>bcrypt는 비밀번호 암호화에 자주 사용되는 라이브러리</li>
<li>해시 함수를 활용하여 강력한 보안을 제공한다.</li>
<li>중요한 요소<ul>
<li>단방향 암호화를 사용하여 저장된 비밀번호를 보호하고, 해시값 비교를 통해 비밀번호 검증을 수행한다.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="bcrypt란">bcrypt란?</h2>
<ul>
<li><code>bcrypt</code>는<code>Blowfish</code>암호화 알고리즘 기반의 단방향 암호화 라이브러리</li>
<li>특히 bcrypt는 실제 애플리케이션에서 쉽게 구현할 수 있으며, 보안 강화를 위한 필수적인 도구로 사용하고 있다.</li>
</ul>
<h4 id="과정">과정</h4>
<ul>
<li>보안성이 높은 해시 함수로, 솔트(Salt)를 적용하여 무작위 해시값을 생성 -&gt; 반복 연산을 통해 해시 생성 속도를 조절 = 결과적으로 해킹을 어렵게 만들어 준다.</li>
</ul>
<hr>
<h2 id="bcrypt-구성">bcrypt 구성</h2>
<ol>
<li><strong>비밀번호 해싱</strong><ul>
<li>hashSync: 비밀번호를 해시화</li>
</ul>
</li>
<li><strong>비밀번호 검증</strong><ul>
<li>compareSync: 입력한 비밀번호와 저장된 해시값을 비교하여 일치 여부를 검증</li>
</ul>
</li>
</ol>
<hr>
<h2 id="사용한-코드">사용한 코드</h2>
<h3 id="bcrypt-코드-예제">bcrypt 코드 예제</h3>
<pre><code class="language-javascript">const bcrypt = require(&quot;bcrypt&quot;);

const saltRounds = 10; // 솔트 라운드 설정

// 비밀번호 해싱 함수
// 설명: 입력된 비밀번호를 솔트를 포함하여 해시화합니다.
function hashPw(pw) {
  return bcrypt.hashSync(pw, saltRounds);
}

// 비밀번호 검증 함수
// 설명: 입력된 비밀번호와 저장된 해시값을 비교합니다.
function comparePw(inputPw, hashedPw) {
  return bcrypt.compareSync(inputPw, hashedPw); // true 또는 false 반환
}

// 테스트용 코드
const originalPw = &quot;1234&quot;; // 원본 비밀번호
const hashedPw = hashPw(originalPw); // 해시화된 비밀번호
console.log(&quot;암호화된 비밀번호&quot;, hashedPw);

// 비밀번호 검증
const isMatch = comparePw(&quot;1234&quot;, hashedPw); // 올바른 비밀번호
const isMatch2 = comparePw(&quot;12345&quot;, hashedPw); // 틀린 비밀번호

console.log(&quot;비밀번호 일치? &gt;&gt;&quot;, isMatch); // true
console.log(&quot;비밀번호 일치? &gt;&gt;&quot;, isMatch2); // false</code></pre>
<hr>
<h2 id="코드-설명">코드 설명</h2>
<ol>
<li><strong>비밀번호 해싱</strong>:<ul>
<li><code>hashSync(pw, saltRounds)</code>: 비밀번호를 입력받아 솔트 라운드를 적용하여 해시값을 생성</li>
<li>솔트 라운드가 높을수록 해시값 생성 시간이 증가 -&gt; 보안 수준 강화</li>
</ul>
</li>
<li><strong>비밀번호 검증</strong>:<ul>
<li><code>compareSync(inputPw, hashedPw)</code>: 사용자가 입력한 비밀번호를 해시화한 후 저장된 해시값과 비교하여 일치 여부를 반환</li>
<li>올바른 비밀번호는 <code>true</code>를 반환</li>
<li>잘못된 비밀번호는 <code>false</code>를 반환</li>
</ul>
</li>
</ol>
<hr>
<h3 id="결론">결론</h3>
<ul>
<li><code>bcrypt</code> 모듈을 활용한 암호화 학습</li>
</ul>
<h3 id="핵심-요약">핵심 요약</h3>
<ol>
<li><strong>단방향 암호화</strong>를 통해 복호화가 불가능한 강력한 해시값을 생성</li>
<li>솔트와 반복 연산을 사용하여 외부 공격을 어렵게 만든다.</li>
<li>비밀번호 검증 기능을 제공하여 안전하게 로그인 프로세스를 구현할 수 있다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[학습 - 비밀번호 암호화 : crypto]]></title>
            <link>https://velog.io/@yoonsuk_choi/%ED%95%99%EC%8A%B5-%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8-%EC%95%94%ED%98%B8%ED%99%94-crypto</link>
            <guid>https://velog.io/@yoonsuk_choi/%ED%95%99%EC%8A%B5-%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8-%EC%95%94%ED%98%B8%ED%99%94-crypto</guid>
            <pubDate>Tue, 07 Jan 2025 16:42:18 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/yoonsuk_choi/post/fdd2610f-a6e4-468b-9f73-e000b2a150aa/image.JPG" alt=""></p>
<h2 id="암호화-crypto">암호화 crypto</h2>
<ul>
<li>암호화는 데이터를 보호하기 위해 특정 알고리즘을 사용하여 원본 정보를 변환하는 과정이다. </li>
<li>이를 통해 데이터는 외부 접근으로부터 안전하게 보호되며, <code>허가된 사용자</code> 만이 정보를 복호화할 수 있다.</li>
</ul>
<hr>
<h2 id="crypto란">crypto란?</h2>
<ul>
<li><code>crypto</code>는 Node.js에서 제공하는 내장 모듈 : 암호화와 복호화 기능을 제공</li>
<li>주요 기능 : 해싱, 단방향 암호화, 양방향 암호화</li>
</ul>
<hr>
<h2 id="crypto-구성">crypto 구성</h2>
<ol>
<li><p><strong>단방향 암호화</strong></p>
<ul>
<li>createHash: 해시 함수를 사용하여 암호화 (복호화 불가)</li>
<li>pbkdf2Sync: 솔트와 반복을 통해 강력한 해시 암호화 구현</li>
</ul>
</li>
<li><p><strong>양방향 암호화</strong></p>
<ul>
<li>createCipheriv: 암호화 기능 제공</li>
<li>createDecipheriv: 복호화 기능 제공</li>
</ul>
</li>
</ol>
<hr>
<h3 id="사용한-코드">사용한 코드</h3>
<h3 id="단방향-암호화-코드-예제">단방향 암호화 코드 예제</h3>
<pre><code class="language-javascript">const crypto = require(&quot;crypto&quot;);

// createHash 예제
const createHashPW = (pw) =&gt; {
  return crypto.createHash(&quot;sha512&quot;).update(pw).digest(&quot;base64&quot;);
};

console.log(createHashPW(&quot;1234&quot;));

// pbkdf2Sync 예제
function saltAndHashPw(pw) {
  const salt = crypto.randomBytes(16).toString(&quot;base64&quot;);
  const iterations = 100;
  const keylen = 64;
  const algorithm = &quot;sha512&quot;;

  const hash = crypto
    .pbkdf2Sync(pw, salt, iterations, keylen, algorithm)
    .toString(&quot;base64&quot;);
  return { salt, hash };
}

console.log(saltAndHashPw(&quot;1234&quot;));

// 비밀번호 검증
function checkPw(inputPw, savedSalt, savedHash) {
  const iterations = 100;
  const keylen = 64;
  const algorithm = &quot;sha512&quot;;

  const hash = crypto
    .pbkdf2Sync(inputPw, savedSalt, iterations, keylen, algorithm)
    .toString(&quot;base64&quot;);

  return hash === savedHash;
}

const data = saltAndHashPw(&quot;qwer1234&quot;);
console.log(checkPw(&quot;qwer1234&quot;, data.salt, data.hash));</code></pre>
<hr>
<h3 id="양방향-암호화-코드-예제">양방향 암호화 코드 예제</h3>
<pre><code class="language-javascript">const key = crypto.randomBytes(32);
const iv = crypto.randomBytes(16);
const algorithm = &quot;aes-256-cbc&quot;;
const originalMessage = &quot;hello, world!&quot;;

// 암호화
function encrypt(text) {
  const cipher = crypto.createCipheriv(algorithm, key, iv);
  let encrypted = cipher.update(text, &quot;utf8&quot;, &quot;base64&quot;);
  encrypted += cipher.final(&quot;base64&quot;);
  return encrypted;
}

// 복호화
function decrypt(encryptedText) {
  const decipher = crypto.createDecipheriv(algorithm, key, iv);
  let decrypted = decipher.update(encryptedText, &quot;base64&quot;, &quot;utf8&quot;);
  decrypted += decipher.final(&quot;utf8&quot;);
  return decrypted;
}

const encryptedMessage = encrypt(originalMessage);
console.log(&quot;암호화된 문장&quot;, encryptedMessage);
const decryptedMessage = decrypt(encryptedMessage);
console.log(&quot;복호화된 문장&quot;, decryptedMessage);</code></pre>
<hr>
<h3 id="결론">결론</h3>
<ul>
<li><code>crypto</code> 모듈을 활용한 단방향 및 양방향 암호화 학습<h3 id="핵심-요약">핵심 요약</h3>
<ul>
<li>단방향 암호화는 비밀번호 저장과 같은 데이터 검증에 유용하다. (해시 생성 및 검증)</li>
<li>양방향 암호화는 데이터를 전송하거나 저장할 때 암호화 및 복호화가 필요할 때 유용하다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[학습 - 비밀번호 암호화]]></title>
            <link>https://velog.io/@yoonsuk_choi/%ED%95%99%EC%8A%B5-%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8-%EC%95%94%ED%98%B8%ED%99%94-fryum4he</link>
            <guid>https://velog.io/@yoonsuk_choi/%ED%95%99%EC%8A%B5-%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8-%EC%95%94%ED%98%B8%ED%99%94-fryum4he</guid>
            <pubDate>Tue, 07 Jan 2025 16:27:25 GMT</pubDate>
            <description><![CDATA[<h2 id="암호화란">암호화란?</h2>
<ul>
<li>암호화는 데이터를 보호하기 위해 원본 정보를 특정 알고리즘을 사용하여 변환하는 과정</li>
<li>데이터는 외부의 접근으로부터 안전하게 보호되며, 허가된 사용자만이 원본 정보와 접촉 가능</li>
</ul>
<hr>
<h3 id="암호화-종류">암호화 종류</h3>
<p>암호화에는 다양한 방식이 존재하며, 대표적으로 <strong>crypto</strong>와 <strong>bcrypt</strong>가 사용됩니다.</p>
<ul>
<li><strong>crypto</strong>: <ul>
<li>빠른 속도와 높은 보안성을 제공하는 암호화 모듈</li>
<li>주로 양방향 암호화에 사용</li>
</ul>
</li>
<li><strong>bcrypt</strong>: <ul>
<li>해시 기반의 단방향 암호화 모듈</li>
<li>비밀번호 저장에 주로 사용</li>
<li>높은 보안성
<img src="https://velog.velcdn.com/images/yoonsuk_choi/post/bd684de2-47f9-4fc8-9ef9-330058307079/image.JPG" alt=""></li>
</ul>
</li>
</ul>
<hr>
<h2 id="단방향-암호화">단방향 암호화</h2>
<h3 id="특징">특징</h3>
<ul>
<li>한 번 암호화된 데이터는 복호화가 불가능</li>
<li>주로 비밀번호 저장에 사용</li>
</ul>
<h4 id="해시hash">해시(Hash)</h4>
<ul>
<li>해시(Hash)란 해시 함수에 의해 얻어지는 고정된 크기의 데이터 값<ul>
<li><strong>해시 함수 (Hash Function)</strong> : 임의의 크기의 데이터를 고정된 크기로 변환하는 알고리즘</li>
<li><strong>키(Key)</strong> : 매핑 전 원본 데이터 값</li>
<li><strong>해시 값(Hash Value)</strong> : 매핑 후 변환된 데이터 값</li>
<li><strong>해싱(Hashing)</strong> : 데이터를 변환하는 과정</li>
</ul>
</li>
</ul>
<hr>
<h2 id="양방향-암호화">양방향 암호화</h2>
<h3 id="특징-1">특징</h3>
<ul>
<li>암호화된 데이터를 원본으로 복호화 가능</li>
<li>보안성과 성능의 균형을 고려한 암호화 방식</li>
</ul>
<h3 id="대칭키-암호화-알고리즘">대칭키 암호화 알고리즘</h3>
<ul>
<li>암호화와 복호화에 동일한 키를 사용</li>
<li>키가 외부에 노출될 경우 보안이 한번에 위험해 질 수 있다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[학습 - JWT의 학습]]></title>
            <link>https://velog.io/@yoonsuk_choi/%ED%95%99%EC%8A%B5-JWT%EC%9D%98-%ED%95%99%EC%8A%B5</link>
            <guid>https://velog.io/@yoonsuk_choi/%ED%95%99%EC%8A%B5-JWT%EC%9D%98-%ED%95%99%EC%8A%B5</guid>
            <pubDate>Thu, 02 Jan 2025 15:35:07 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/yoonsuk_choi/post/bd3b2161-a8e5-4663-b9b5-6cdc6de8ca08/image.JPG" alt=""></p>
<h2 id="1-jwt란">1. JWT란?</h2>
<ul>
<li>JWT(JSON Web Token)는 클라이언트와 서버 간의 안전한 정보 교환을 위한 토큰 기반 인증 메커니즘</li>
<li>JWT는 사용자 인증 및 정보 전송 시 신뢰성을 보장해준다.<h3 id="jwt-작동방식">JWT 작동방식</h3>
<img src="https://velog.velcdn.com/images/yoonsuk_choi/post/1afb295f-17c2-4c02-bebc-e34a098cc66e/image.jpg" alt=""><h2 id="2-jwt-구성">2. JWT 구성</h2>
</li>
<li><strong>Header:</strong> 토큰의 타입과 해시 알고리즘 정보가 포함</li>
<li><strong>Payload:</strong> 실제 정보 데이터가 포함되며, 클레임(claim)이라 불리는 키-값 쌍으로 이루어져 있음</li>
<li><strong>Signature:</strong> 토큰의 무결성을 검증하기 위한 서명 부분으로, Header와 Payload의 조합에 비밀 키를 사용해 생성</li>
</ul>
<h2 id="3-코드-설명">3. 코드 설명</h2>
<h3 id="31-서버-코드appjs">3.1 서버 코드(app.js)</h3>
<pre><code class="language-javascript">const express = require(&quot;express&quot;);
const jwt = require(&quot;jsonwebtoken&quot;);
const app = express();
const PORT = 8080;
const SECRET = &quot;rMaRSWeDfz2NDB7H&quot;; // 보안 강화를 위해 .env 파일에 저장 권장

app.set(&quot;view engine&quot;, &quot;ejs&quot;);
app.use(express.urlencoded({ extended: false }));
app.use(express.json());

const userInfo = {
  id: &quot;cocoa&quot;,
  pw: &quot;1234&quot;,
  name: &quot;코코아&quot;,
  age: 18,
};

app.get(&quot;/&quot;, (req, res) =&gt; res.render(&quot;index&quot;));
app.get(&quot;/login&quot;, (req, res) =&gt; res.render(&quot;login&quot;));

app.post(&quot;/login&quot;, (req, res) =&gt; {
  const { id, pw } = req.body;
  if (id === userInfo.id &amp;&amp; pw === userInfo.pw) {
    const token = jwt.sign({ id }, SECRET);
    res.send({ result: true, token });
  } else {
    res.send({ result: false, message: &quot;로그인 정보가 올바르지 않습니다.&quot; });
  }
});

app.post(&quot;/token&quot;, (req, res) =&gt; {
  const token = req.headers.authorization?.split(&quot; &quot;)[1];
  if (token) {
    try {
      const auth = jwt.verify(token, SECRET);
      if (auth.id === userInfo.id) {
        res.send({ result: true, name: userInfo.name });
      }
    } catch {
      res.status(401).send({ result: false, message: &quot;인증된 회원이 아닙니다.&quot; });
    }
  } else {
    res.redirect(&quot;/login&quot;);
  }
});

app.listen(PORT, () =&gt; console.log(`http://localhost:${PORT}`));</code></pre>
<h3 id="32-로그인-페이지loginejs">3.2 로그인 페이지(login.ejs)</h3>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot;&gt;
&lt;head&gt;
  &lt;title&gt;login&lt;/title&gt;
  &lt;script src=&quot;https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js&quot;&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;h1&gt;로그인&lt;/h1&gt;
  &lt;form name=&quot;login-form&quot;&gt;
    &lt;input type=&quot;text&quot; id=&quot;id&quot; placeholder=&quot;ID를 입력하세요&quot; /&gt;
    &lt;input type=&quot;password&quot; id=&quot;pw&quot; placeholder=&quot;비밀번호를 입력하세요&quot; /&gt;
    &lt;button type=&quot;button&quot; onclick=&quot;login()&quot;&gt;로그인&lt;/button&gt;
  &lt;/form&gt;
  &lt;script&gt;
    async function login() {
      const form = document.forms[&quot;login-form&quot;];
      const response = await axios.post(&quot;/login&quot;, {
        id: form.id.value,
        pw: form.pw.value,
      });
      const { result, token, message } = response.data;
      if (result) {
        localStorage.setItem(&quot;login&quot;, token);
        document.location.href = &quot;/&quot;;
      } else {
        alert(message);
        form.reset();
      }
    }
  &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<h3 id="33-메인-페이지indexejs">3.3 메인 페이지(index.ejs)</h3>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot;&gt;
&lt;head&gt;
  &lt;title&gt;JWT&lt;/title&gt;
  &lt;script src=&quot;https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js&quot;&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;h1&gt;JWT&lt;/h1&gt;
  &lt;div id=&quot;info&quot;&gt;&lt;/div&gt;
  &lt;script&gt;
    (async function () {
      const token = localStorage.getItem(&quot;login&quot;);
      const info = document.getElementById(&quot;info&quot;);
      let data;
      if (!token) {
        data = &#39;&lt;a href=&quot;/login&quot;&gt;로그인&lt;/a&gt;&#39;;
      } else {
        const response = await axios.post(&quot;/token&quot;, {}, {
          headers: { Authorization: `Bearer ${token}` },
        });
        if (response.data.result) {
          data = `&lt;p&gt;${response.data.name}님 환영합니다!&lt;/p&gt;
                  &lt;button onclick=&quot;logout();&quot;&gt;로그아웃&lt;/button&gt;`;
        }
      }
      info.innerHTML = data;
    })();

    function logout() {
      localStorage.clear();
      document.location.reload();
    }
  &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<h2 id="4-실행-및-동작-원리">4. 실행 및 동작 원리</h2>
<ol>
<li>사용자는 <strong>로그인 페이지</strong>에서 ID와 비밀번호를 입력</li>
<li>서버는 사용자 정보를 검증한 후, JWT를 생성하여 클라이언트에 전달</li>
<li>클라이언트는 JWT를 <strong>로컬 스토리지</strong>에 저장</li>
<li>이후 요청 시, JWT를 <strong>헤더에 추가</strong>하여 서버로 전송</li>
<li>서버는 JWT의 유효성을 검증하고 사용자 정보를 반환</li>
<li>클라이언트는 인증 성공 여부에 따라 페이지를 렌더링하거나 로그아웃을 처리</li>
</ol>
<h2 id="5-결론">5. 결론</h2>
<ul>
<li>JWT는 보안성과 효율성 면에서 강력한 인증 방식</li>
<li>다양한 웹 애플리케이션에서 사용한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[실습 - session]]></title>
            <link>https://velog.io/@yoonsuk_choi/%EC%8B%A4%EC%8A%B5-session</link>
            <guid>https://velog.io/@yoonsuk_choi/%EC%8B%A4%EC%8A%B5-session</guid>
            <pubDate>Thu, 02 Jan 2025 15:22:01 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/yoonsuk_choi/post/39437ec8-4cd4-45f1-80d3-5600f3db1076/image.JPG" alt=""></p>
<h2 id="1-실습-내용">1. 실습 내용</h2>
<ul>
<li>이번 실습에서는 Node.js의 Express 프레임워크를 이용하여 세션(session)을 활용한 로그인 및 로그아웃 기능을 구현</li>
<li>세션을 통해 사용자의 로그인 상태를 관리하며, 일정 시간이 지나면 자동으로 세션이 만료되도록 설정하는 기능을 학습</li>
</ul>
<hr>
<h2 id="2-실습-결과">2. 실습 결과</h2>
<ol>
<li>사용자 로그인 여부에 따라 페이지에 다른 메시지를 출력.</li>
<li>로그인 시 세션을 생성하여 상태를 유지.</li>
<li>로그아웃 시 세션을 삭제하여 상태 해제.</li>
<li>UI는 EJS 템플릿 엔진을 활용하여 구현.</li>
<li>Bootstrap을 이용한 간단한 네비게이션 바 추가.</li>
</ol>
<hr>
<h2 id="3-사용한-코드-및-기능-설명">3. 사용한 코드 및 기능 설명</h2>
<h3 id="31-appjs">3.1 app.js</h3>
<ul>
<li><strong>기능:</strong> 서버 초기화, EJS 설정, 정적 파일 제공, 페이지 렌더링, 로그인/로그아웃 처리 준비.<pre><code class="language-javascript">const express = require(&quot;express&quot;);
const app = express();
const PORT = 8080;
</code></pre>
</li>
</ul>
<p>app.set(&quot;view engine&quot;, &quot;ejs&quot;);
app.use(&quot;/static&quot;, express.static(__dirname + &quot;/static&quot;));</p>
<p>// 세션 설정, 10분 뒤 세션 종료하도록</p>
<p>// 홈 페이지 렌더링
app.get(&quot;/&quot;, (req, res) =&gt; {
  res.render(&quot;index&quot;); // 홈 화면 렌더링
});</p>
<p>// 로그인 페이지 렌더링
app.get(&quot;/login&quot;, (req, res) =&gt; {
  res.render(&quot;login&quot;); // 로그인 화면 렌더링
});</p>
<p>const userInfo = {
  userId: &quot;cocoa&quot;,
  userPw: &quot;1234&quot;,
};</p>
<p>// 로그인 요청 처리
app.post(&quot;/login&quot;, (req, res) =&gt; {
  // 로그인 로직 (세션 추가 예정)
});</p>
<p>// 로그아웃 요청 처리
app.get(&quot;/logout&quot;, (req, res) =&gt; {
  // 로그아웃 로직 (세션 삭제 예정)
});</p>
<p>// 서버 실행
app.listen(PORT, () =&gt; {
  console.log(<code>http://localhost:${PORT}</code>);
});</p>
<pre><code>
### 3.2 index.ejs
- **기능:** 홈 화면 구성 및 로그인 여부에 따른 메시지 표시.
```html
&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot;&gt;
&lt;head&gt;
  &lt;%- include(&#39;include&#39;, {title: &#39;Home&#39;}) %&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;%- include(&#39;header&#39;) %&gt;
  &lt;h1&gt;실습. Session 연습&lt;/h1&gt;
  &lt;p&gt;여기는 Home 입니다! 🏡&lt;/p&gt;

  &lt;%# if (로그인 되었다면) { %&gt;
  &lt;p&gt;~~~ 님 환영합니다 🙌🏻&lt;/p&gt;
  &lt;%# } else { %&gt;
  &lt;p&gt;로그인을 아직 하지 않으셨군요! 로그인을 진행해보세요!&lt;/p&gt;
  &lt;%# } %&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h3 id="33-loginejs">3.3 login.ejs</h3>
<ul>
<li><p><strong>기능:</strong> 로그인 폼 제공 및 로그인 요청 전송.</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot;&gt;
&lt;head&gt;
&lt;%- include(&#39;include&#39;, {title: &#39;Login&#39;}) %&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;/static/login.css&quot; /&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;div class=&quot;login-box&quot;&gt;
  &lt;h1&gt;로그인&lt;/h1&gt;
  &lt;p&gt;
    참고! 정답 아이디는 &lt;b&gt;cocoa&lt;/b&gt; 이고&lt;br /&gt;
    비밀번호는 &lt;b&gt;1234&lt;/b&gt; 입니다.
  &lt;/p&gt;

  &lt;!-- 로그인 폼 --&gt;
  &lt;form action=&quot;/login&quot; method=&quot;POST&quot;&gt;
    &lt;div class=&quot;input-group mb-3&quot;&gt;
      &lt;span class=&quot;input-group-text&quot; id=&quot;id&quot;&gt;ID&lt;/span&gt;
      &lt;input type=&quot;text&quot; class=&quot;form-control&quot; name=&quot;id&quot; /&gt;
    &lt;/div&gt;
    &lt;div class=&quot;input-group mb-3&quot;&gt;
      &lt;span class=&quot;input-group-text&quot; id=&quot;pw&quot;&gt;PW&lt;/span&gt;
      &lt;input type=&quot;password&quot; class=&quot;form-control&quot; name=&quot;pw&quot; /&gt;
    &lt;/div&gt;

    &lt;button type=&quot;submit&quot; class=&quot;btn btn-dark&quot;&gt;Login&lt;/button&gt;
    &lt;br /&gt;
    &lt;a href=&quot;/&quot;&gt;Home 이동하기&lt;/a&gt;
  &lt;/form&gt;
&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
</li>
</ul>
<h3 id="34-headerejs">3.4 header.ejs</h3>
<ul>
<li><strong>기능:</strong> 네비게이션 바 생성 및 로그인 상태에 따라 메뉴 항목 변경.<pre><code class="language-html">&lt;nav class=&quot;navbar navbar-expand-lg bg-dark&quot; data-bs-theme=&quot;dark&quot;&gt;
&lt;div class=&quot;container-fluid&quot;&gt;
  &lt;a class=&quot;navbar-brand&quot; href=&quot;#&quot;&gt;Codingon&lt;/a&gt;
  &lt;button class=&quot;navbar-toggler&quot; type=&quot;button&quot; data-bs-toggle=&quot;collapse&quot; data-bs-target=&quot;#myHeader&quot;&gt;
    &lt;span class=&quot;navbar-toggler-icon&quot;&gt;&lt;/span&gt;
  &lt;/button&gt;
  &lt;div class=&quot;collapse navbar-collapse&quot; id=&quot;myHeader&quot;&gt;
    &lt;ul class=&quot;navbar-nav me-auto mb-2 mb-lg-0&quot;&gt;
      &lt;li class=&quot;nav-item&quot;&gt;
        &lt;a class=&quot;nav-link active&quot; href=&quot;#&quot;&gt;Menu1&lt;/a&gt;
      &lt;/li&gt;
      &lt;li class=&quot;nav-item&quot;&gt;
        &lt;a class=&quot;nav-link active&quot; href=&quot;#&quot;&gt;Menu2&lt;/a&gt;
      &lt;/li&gt;
      &lt;%#if (로그인 되었다면) { %&gt;
      &lt;li class=&quot;nav-item&quot;&gt;
        &lt;a class=&quot;nav-link active&quot; href=&quot;/logout&quot;&gt;Logout&lt;/a&gt;
      &lt;/li&gt;
      &lt;%# } else { %&gt;
      &lt;li class=&quot;nav-item&quot;&gt;
        &lt;a class=&quot;nav-link active&quot; href=&quot;/login&quot;&gt;Login&lt;/a&gt;
      &lt;/li&gt;
      &lt;%# } %&gt;
    &lt;/ul&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;/nav&gt;</code></pre>
</li>
</ul>
<hr>
<h2 id="4-결론">4. 결론</h2>
<ul>
<li>기본적인 세션 관리 개념과 사용법 중점으로 실습</li>
<li>Express 프레임워크를 사용하여 세션 기반 로그인 상태를 유지하거나 종료하는 기능을 구현</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[실습 - Cookie]]></title>
            <link>https://velog.io/@yoonsuk_choi/%EC%8B%A4%EC%8A%B5-Cookie</link>
            <guid>https://velog.io/@yoonsuk_choi/%EC%8B%A4%EC%8A%B5-Cookie</guid>
            <pubDate>Thu, 02 Jan 2025 14:41:38 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/yoonsuk_choi/post/619d0b14-5886-42d1-907e-20df0df08572/image.JPG" alt=""></p>
<h2 id="실습---cookie">실습 - Cookie</h2>
<h3 id="실습-내용">실습 내용</h3>
<ul>
<li>쿠키를 이용하여 특정 조건을 만족할 경우 팝업창을 숨기는 기능을 구현</li>
<li>사용자가 &#39;오늘 그만 보기&#39;를 체크한 후 닫기 버튼을 클릭하면, 해당 정보가 쿠키에 저장 -&gt; 브라우저를 새로고침하거나 다시 실행해도 팝업창이 나타나지 않도록 설정</li>
</ul>
<h3 id="실습-결과">실습 결과</h3>
<ol>
<li>페이지에 접속하면 광고 팝업 창이 뜬다.</li>
<li>&#39;오늘 하루 보지 않음&#39;을 체크하고 닫기 버튼을 클릭하면 쿠키가 생성</li>
<li>브라우저를 재시작하거나 새로고침해도 쿠키가 유지되면 팝업이 나타나지 않음</li>
<li>쿠키를 삭제한 경우에는 페이지를 새로고침하면 팝업창이 다시 나타난다.</li>
</ol>
<hr>
<h2 id="사용한-코드">사용한 코드</h2>
<h3 id="appjs">app.js</h3>
<pre><code class="language-javascript">const express = require(&quot;express&quot;);
const app = express();

const PORT = 8080;

app.set(&quot;view engine&quot;, &quot;ejs&quot;);

// 쿠키 미들웨어 설정 예정
app.get(&quot;/&quot;, (req, res) =&gt; {
  res.render(&quot;index&quot;);
  // 쿠키값 가져오기 및 index.ejs에 보내기
  // res.render(&quot;index&quot;, {popup:쿠키값});
});

app.post(&quot;/set-cookie&quot;, (req, res) =&gt; {
  // 쿠키 생성 구현 예정
  res.send(&quot;쿠키 생성 성공!!!&quot;);
});

app.listen(PORT, () =&gt; {
  console.log(`http://localhost:${PORT}`);
});</code></pre>
<h3 id="indexejs">index.ejs</h3>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot; /&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
    &lt;title&gt;실습&lt;/title&gt;

    &lt;!-- Bootstrap 및 Axios 라이브러리 포함 --&gt;
    &lt;link
      href=&quot;https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css&quot;
      rel=&quot;stylesheet&quot;
    /&gt;
    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js&quot;&gt;&lt;/script&gt;
    &lt;script src=&quot;https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js&quot;&gt;&lt;/script&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;실습. Cookie 연습&lt;/h1&gt;
    &lt;p&gt;
      페이지 접속했을 때 팝업 창이 보이며, &#39;오늘 그만 보기&#39;를 체크 후 닫기를 하면 창이 다시 나타나지 않도록 구현합니다.
    &lt;/p&gt;

    &lt;!-- Modal: 팝업창 --&gt;
    &lt;div
      class=&quot;modal fade&quot;
      id=&quot;exampleModal&quot;
      tabindex=&quot;-1&quot;
      aria-labelledby=&quot;exampleModalLabel&quot;
      aria-hidden=&quot;true&quot;
    &gt;
      &lt;div class=&quot;modal-dialog&quot;&gt;
        &lt;div class=&quot;modal-content&quot;&gt;
          &lt;div class=&quot;modal-header&quot;&gt;
            &lt;h5 class=&quot;modal-title&quot; id=&quot;exampleModalLabel&quot;&gt;cookie 실습&lt;/h5&gt;
            &lt;button type=&quot;button&quot; class=&quot;btn-close&quot; data-bs-dismiss=&quot;modal&quot;&gt;&lt;/button&gt;
          &lt;/div&gt;
          &lt;div class=&quot;modal-body&quot;&gt;
            광고! 쿠키 실습입니다.
            &lt;div class=&quot;text-end mt-3&quot;&gt;
              &lt;input type=&quot;checkbox&quot; id=&quot;cookie&quot; /&gt;
              &lt;label for=&quot;cookie&quot;&gt;오늘 하루 보지 않음.&lt;/label&gt;
            &lt;/div&gt;
          &lt;/div&gt;
          &lt;div class=&quot;modal-footer&quot;&gt;
            &lt;button type=&quot;button&quot; class=&quot;btn btn-secondary&quot; onclick=&quot;closeModal();&quot;&gt;닫기&lt;/button&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;script&gt;
      const myModal = new bootstrap.Modal(&quot;#exampleModal&quot;);
      myModal.show(); // 팝업창 보임

      async function closeModal() {
        const isChecked = document.getElementById(&quot;cookie&quot;).checked;
        if (isChecked) {
          await axios.post(&quot;/set-cookie&quot;);
        }
        myModal.hide(); // 팝업창 닫기
      }
    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<hr>
<h3 id="코드-설명">코드 설명</h3>
<ol>
<li><strong>app.js</strong>에서는 Express를 사용하여 서버를 설정하고, 쿠키를 관리하는 기능을 추가할 예정</li>
<li><strong>index.ejs</strong>에서는 Bootstrap을 사용하여 팝업 모달 창을 생성하고, JavaScript 코드로 쿠키 설정 및 팝업 동작을 관리</li>
</ol>
<hr>
<h2 id="결론">결론</h2>
<p>쿠키를 활용하여 브라우저에서 상태를 유지하는 방법 실습.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[학습 - 세션(session)의 학습]]></title>
            <link>https://velog.io/@yoonsuk_choi/%ED%95%99%EC%8A%B5-%EC%84%B8%EC%85%98session%EC%9D%98-%ED%95%99%EC%8A%B5</link>
            <guid>https://velog.io/@yoonsuk_choi/%ED%95%99%EC%8A%B5-%EC%84%B8%EC%85%98session%EC%9D%98-%ED%95%99%EC%8A%B5</guid>
            <pubDate>Thu, 02 Jan 2025 14:30:07 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/yoonsuk_choi/post/b3c36ad4-4799-41bb-a9ac-78605ee0e43d/image.JPG" alt=""></p>
<h2 id="1-session이란">1. Session이란?</h2>
<ul>
<li>Session(세션)은 클라이언트와 서버 간의 상태를 유지하기 위해 사용</li>
</ul>
<h2 id="2-session의-동작-방식">2. Session의 동작 방식</h2>
<ul>
<li><strong>세션 생성</strong>: 클라이언트가 서버에 처음 접속할 때 세션 ID가 생성되며, 이 ID는 클라이언트의 쿠키에 저장</li>
<li><strong>세션 확인</strong>: 클라이언트가 서버에 요청을 보낼 때, 저장된 세션 ID를 사용하여 서버는 클라이언트의 상태 정보를 확인</li>
<li><strong>세션 삭제</strong>: 일정 시간 동안 활동이 없거나 로그아웃 시 세션은 삭제</li>
</ul>
<hr>
<h2 id="3-코드-설명">3. 코드 설명</h2>
<h3 id="사용한-코드-sessionjs">사용한 코드: <code>session.js</code></h3>
<pre><code class="language-javascript">const express = require(&quot;express&quot;);
const session = require(&quot;express-session&quot;);
const app = express();
const PORT = 8080;

app.set(&quot;view engine&quot;, &quot;ejs&quot;);

// session 미들웨어 등록
app.use(
  session({
    secret: &quot;secret Key&quot;,
    resave: false,
    saveUninitialized: false,
    cookie: {
      httpOnly: true,
      maxAge: 10 * 60 * 1000, // 10분짜리 세션 쿠키
    },
  }),
);

app.get(&quot;/&quot;, (req, res) =&gt; {
  res.render(&quot;session&quot;);
});

// 세션 설정
app.get(&quot;/set&quot;, (req, res) =&gt; {
  req.session.name = &quot;allie&quot;;
  res.send(&quot;session 설정 완료&quot;);
});

// 세션 확인(가져오기)
app.get(&quot;/get&quot;, (req, res) =&gt; {
  console.log(req.session);
  res.send({ id: req.sessionID, name: req.session.name });
});

// 세션 삭제
app.get(&quot;/destroy&quot;, (req, res) =&gt; {
  req.session.destroy((err) =&gt; {
    if (err) throw err;
    res.send(&quot;세션 삭제 완료&quot;);
  });
});

app.listen(PORT, () =&gt; {
  console.log(`http://localhost:${PORT}`);
});</code></pre>
<h3 id="코드-설명">코드 설명</h3>
<ol>
<li><p><strong>세션 미들웨어 등록</strong>:</p>
<ul>
<li><code>secret</code>: 세션 데이터를 암호화할 때 사용되는 키값.</li>
<li><code>resave</code>: 세션이 수정되지 않으면 저장하지 않음.</li>
<li><code>saveUninitialized</code>: 저장할 데이터가 없어도 세션을 초기화.</li>
<li><code>cookie</code>: 세션 쿠키의 옵션 설정 (HTTPOnly, 만료 시간 등).</li>
</ul>
</li>
<li><p><strong>세션 설정</strong>:</p>
<ul>
<li><code>/set</code>: 클라이언트의 세션에 <code>name: allie</code> 값을 저장.</li>
</ul>
</li>
<li><p><strong>세션 확인</strong>:</p>
<ul>
<li><code>/get</code>: 세션 ID와 저장된 데이터를 조회.</li>
</ul>
</li>
<li><p><strong>세션 삭제</strong>:</p>
<ul>
<li><code>/destroy</code>: 세션 데이터를 삭제.</li>
</ul>
</li>
</ol>
<hr>
<h2 id="4-실습-결과">4. 실습 결과</h2>
<h3 id="사용한-코드-sessionejs">사용한 코드: <code>session.ejs</code></h3>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
    &lt;title&gt;session&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;session 확인하기&lt;/h1&gt;
    &lt;a href=&quot;/set&quot;&gt;세션 설정&lt;/a&gt;
    &lt;a href=&quot;/get&quot;&gt;세션 확인&lt;/a&gt;
    &lt;a href=&quot;/destroy&quot;&gt;세션 삭제&lt;/a&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<h3 id="설명">설명</h3>
<ul>
<li><strong>세션 설정 버튼</strong>: <code>/set</code> 경로로 이동하여 세션 설정.</li>
<li><strong>세션 확인 버튼</strong>: <code>/get</code> 경로로 이동하여 현재 세션 확인.</li>
<li><strong>세션 삭제 버튼</strong>: <code>/destroy</code> 경로로 이동하여 세션 삭제.</li>
</ul>
<hr>
<h3 id="5-결론-및-학습-포인트">5. 결론 및 학습 포인트</h3>
<h4 id="학습-포인트">학습 포인트</h4>
<ol>
<li>세션을 활용하여 클라이언트의 상태 정보를 유지하고 관리할 수 있음.</li>
<li>세션 미들웨어 설정을 통해 보안 및 성능을 최적화할 수 있음.</li>
<li>세션의 설정, 조회, 삭제를 코드로 구현하고 테스트함.</li>
</ol>
<h4 id="결론">결론</h4>
<p>세션 관리 시스템을 활용하면 사용자 로그인 상태나 일시적인 데이터 저장 등을 쉽게 구현할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[학습 - 쿠키(Cookie)의 학습]]></title>
            <link>https://velog.io/@yoonsuk_choi/%ED%95%99%EC%8A%B5-%EC%BF%A0%ED%82%A4Cookie%EC%9D%98-%ED%95%99%EC%8A%B5</link>
            <guid>https://velog.io/@yoonsuk_choi/%ED%95%99%EC%8A%B5-%EC%BF%A0%ED%82%A4Cookie%EC%9D%98-%ED%95%99%EC%8A%B5</guid>
            <pubDate>Thu, 02 Jan 2025 14:24:25 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/yoonsuk_choi/post/a6418fb6-c96f-4cfe-9f54-5d972416a97c/image.JPG" alt=""></p>
<h2 id="cookie란">Cookie란?</h2>
<ul>
<li>쿠키(Cookie)는 웹 서버가 사용자의 웹 브라우저에 저장하는 작은 데이터 조각 </li>
<li>쿠키는 사용자 인증, 세션 관리, 사용자 맞춤 설정 등을 위해 사용</li>
<li>브라우저가 서버에 HTTP 요청을 보낼 때 쿠키 데이터를 함께 전송</li>
</ul>
<h2 id="cookie의-동작-방식">Cookie의 동작 방식</h2>
<ol>
<li>사용자가 웹사이트에 방문하면 서버가 쿠키를 생성</li>
<li>서버는 HTTP 응답을 통해 쿠키를 클라이언트(브라우저)에 전송</li>
<li>브라우저는 쿠키를 저장하고 이후 해당 웹사이트에 재접속할 때 요청에 쿠키를 포함</li>
<li>서버는 전송된 쿠키를 읽어 사용자를 식별하거나 이전 설정을 복원</li>
</ol>
<h2 id="pip-설치-명령어">pip 설치 명령어</h2>
<pre><code class="language-bash">npm install express cookie-parser</code></pre>
<h2 id="실습결과">실습결과</h2>
<ul>
<li>Node.js와 Express를 사용하여 쿠키 생성, 조회, 삭제의 기능을 구현 </li>
<li>암호화되지 않은 쿠키와 암호화된 쿠키의 설정과 관리 방법을 비교하며 학습</li>
</ul>
<h2 id="사용한-코드">사용한 코드</h2>
<h3 id="cookiejs">cookie.js</h3>
<pre><code class="language-javascript">const express = require(&quot;express&quot;);
const cookieParser = require(&quot;cookie-parser&quot;);
const app = express();
const PORT = 8080;

// 암호화된 쿠키 사용
app.use(cookieParser(&quot;secret Key&quot;));

app.set(&quot;view engine&quot;, &quot;ejs&quot;);

const cookieConfig = {
  maxAge: 10 * 60 * 1000, // 10분
  httpOnly: true, // 브라우저에서 접근 불가
  signed: true, // 암호화 적용
};

const cookieConfig2 = {
  maxAge: 10 * 60 * 1000,
  httpOnly: true,
  path: &quot;/abc&quot;,
};

app.get(&quot;/abc&quot;, (req, res) =&gt; {
  res.cookie(&quot;abc-page&quot;, &quot;abc page cookie&quot;, cookieConfig2);
  res.render(&quot;cookie-another&quot;);
});

app.get(&quot;/&quot;, (req, res) =&gt; {
  res.render(&quot;cookie&quot;);
});

app.get(&quot;/getCookie&quot;, (req, res) =&gt; {
  console.log(&quot;암호화된 쿠키&quot;, req.signedCookies);
  res.send(req.signedCookies);
});

app.get(&quot;/setCookie&quot;, (req, res) =&gt; {
  res.cookie(&quot;myCookie&quot;, &quot;cookie~~~&quot;, cookieConfig);
  res.send(&quot;set cookie 완료, 응답 종료!&quot;);
});

app.get(&quot;/clearCookie&quot;, (req, res) =&gt; {
  res.clearCookie(&quot;myCookie&quot;, &quot;cookie~~~&quot;, cookieConfig);
  res.send(&quot;clear cookie, 응답 종료!&quot;);
});

app.listen(PORT, () =&gt; {
  console.log(`http://localhost:${PORT}`);
});</code></pre>
<h3 id="cookieejs">cookie.ejs</h3>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
    &lt;title&gt;cookie&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;쿠키 확인하기&lt;/h1&gt;
    &lt;a href=&quot;/setCookie&quot;&gt;쿠키 설정&lt;/a&gt;
    &lt;a href=&quot;/getCookie&quot;&gt;쿠키값 가져오기&lt;/a&gt;
    &lt;a href=&quot;/clearCookie&quot;&gt;쿠키 삭제&lt;/a&gt;
    &lt;script&gt;
      console.log(&quot;document.cookie&quot;, document.cookie);
    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<h3 id="cookie-anotherejs">cookie-another.ejs</h3>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
    &lt;title&gt;Document&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;다른 페이지에서 쿠키 확인할 수 있는지 보기&lt;/h1&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<h3 id="주요-기능-정리">주요 기능 정리</h3>
<ol>
<li><strong>쿠키 설정</strong><ul>
<li>암호화되지 않은 쿠키 및 암호화된 쿠키 설정 가능.</li>
<li>쿠키 수명, 접근 제한, 경로 제한 등 다양한 옵션 제공.</li>
</ul>
</li>
<li><strong>쿠키 조회</strong><ul>
<li>저장된 쿠키 정보를 콘솔에 출력.</li>
<li>암호화된 쿠키는 <code>req.signedCookies</code>를 통해 확인.</li>
</ul>
</li>
<li><strong>쿠키 삭제</strong><ul>
<li>특정 조건에 맞는 쿠키 삭제 기능 구현.</li>
</ul>
</li>
</ol>
<h3 id="학습-요약">학습 요약</h3>
<ul>
<li>쿠키의 기본 개념과 동작 원리를 이해함.</li>
<li>Node.js의 <code>cookie-parser</code> 미들웨어를 활용하여 쿠키 관리 기능을 구현함.</li>
<li>암호화된 쿠키를 통해 보안성을 강화함.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[학습 - Cookie와 Session 비교]]></title>
            <link>https://velog.io/@yoonsuk_choi/%ED%95%99%EC%8A%B5-Cookie%EC%99%80-Session-%EB%B9%84%EA%B5%90</link>
            <guid>https://velog.io/@yoonsuk_choi/%ED%95%99%EC%8A%B5-Cookie%EC%99%80-Session-%EB%B9%84%EA%B5%90</guid>
            <pubDate>Thu, 02 Jan 2025 14:17:13 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/yoonsuk_choi/post/10d55c8a-01da-4359-9fa3-3a136fd39460/image.JPG" alt=""></p>
<h2 id="1-cookie와-session의-개요">1. Cookie와 Session의 개요</h2>
<h3 id="cookie">Cookie</h3>
<p><img src="https://velog.velcdn.com/images/yoonsuk_choi/post/8b1cd0a2-11bd-42f9-91f5-b590d4abea3b/image.jpg" alt=""></p>
<ul>
<li><strong>정의:</strong> 클라이언트(브라우저)에 저장되는 데이터.</li>
<li><strong>특징:</strong><ul>
<li>키-값 구조.</li>
<li>브라우저 종료 후에도 설정된 유효기간 내에 유지.</li>
<li>클라이언트 측에서 관리되므로 서버 자원을 소모하지 않음.</li>
</ul>
</li>
<li><strong>사용 예시:</strong><ul>
<li>사용자 로그인 정보 저장.</li>
<li>최근 본 상품 기록.</li>
<li>사용자 설정 유지(테마, 언어 등).</li>
</ul>
</li>
</ul>
<h3 id="session">Session</h3>
<ul>
<li><strong>정의:</strong> 서버에 저장되는 사용자 정보.</li>
<li><strong>특징:</strong><ul>
<li>각 사용자에 대해 고유 세션 ID 발급.</li>
<li>서버 측에서 관리되므로 클라이언트가 직접 접근할 수 없음.</li>
<li>브라우저 종료 시 기본적으로 삭제.</li>
</ul>
</li>
<li><strong>사용 예시:</strong><ul>
<li>로그인 상태 유지.</li>
<li>쇼핑몰 장바구니 정보 저장.</li>
<li>보안이 필요한 데이터 처리.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="2-속도와-보안-비교">2. 속도와 보안 비교</h2>
<h3 id="속도">속도</h3>
<ul>
<li><strong>Cookie:</strong><ul>
<li>클라이언트 측 저장으로 서버 부하가 적음.</li>
<li>처리 속도가 빠름.</li>
<li>HTTP 요청 시 자동으로 쿠키 데이터가 전송되므로 네트워크 트래픽 발생 가능.</li>
</ul>
</li>
<li><strong>Session:</strong><ul>
<li>서버 측 관리로 데이터 처리에 시간이 더 소요될 수 있음.</li>
<li>보안 처리가 강화되어 있음.</li>
</ul>
</li>
</ul>
<h3 id="보안">보안</h3>
<ul>
<li><strong>Cookie:</strong><ul>
<li>보안에 취약(클라이언트에서 접근 가능).</li>
<li>암호화 및 보안 설정(HTTPOnly, Secure) 필요.</li>
</ul>
</li>
<li><strong>Session:</strong><ul>
<li>서버 측 저장으로 보안이 강화됨.</li>
<li>세션 ID 도난 방지를 위한 관리 필요.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="3-cookie와-session의-활용-분야">3. Cookie와 Session의 활용 분야</h2>
<ul>
<li><strong>Cookie:</strong><ul>
<li>가벼운 데이터 저장.</li>
<li>장기적인 사용자 설정 유지.</li>
</ul>
</li>
<li><strong>Session:</strong><ul>
<li>민감한 데이터 관리.</li>
<li>보안이 중요한 작업(로그인, 결제 등).</li>
</ul>
</li>
</ul>
<hr>
<h2 id="4-요약">4. 요약</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>Cookie</th>
<th>Session</th>
</tr>
</thead>
<tbody><tr>
<td>저장 위치</td>
<td>클라이언트(브라우저)</td>
<td>서버</td>
</tr>
<tr>
<td>보안 수준</td>
<td>낮음(암호화 필요)</td>
<td>높음</td>
</tr>
<tr>
<td>데이터 용량 제한</td>
<td>최대 4KB</td>
<td>서버 설정에 따라 다름</td>
</tr>
<tr>
<td>속도</td>
<td>빠름</td>
<td>상대적으로 느림</td>
</tr>
<tr>
<td>사용 예시</td>
<td>사용자 설정, 로그인 유지</td>
<td>로그인 인증, 보안 처리</td>
</tr>
</tbody></table>
<hr>
<h3 id="결론">결론</h3>
<ul>
<li><strong>Cookie</strong>는 빠른 데이터 접근과 간단한 정보 저장에 적합.</li>
<li><strong>Session</strong>은 보안이 중요한 정보 관리에 적합.</li>
<li>상황에 따라 두 가지를 혼합하여 사용할 수 있음.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[ project[Puleus]_(1주차)프로젝트의 시작]]></title>
            <link>https://velog.io/@yoonsuk_choi/projectPuleus-1%EC%A3%BC%EC%B0%A8%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%9D%98-%EC%8B%9C%EC%9E%91</link>
            <guid>https://velog.io/@yoonsuk_choi/projectPuleus-1%EC%A3%BC%EC%B0%A8%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%9D%98-%EC%8B%9C%EC%9E%91</guid>
            <pubDate>Mon, 16 Dec 2024 07:54:00 GMT</pubDate>
            <description><![CDATA[<p>기간 : 24.12.11~ 24.12.15<img src="https://velog.velcdn.com/images/yoonsuk_choi/post/821e02c2-d598-4f1d-bd71-f8d78dfcc6af/image.JPG" alt=""></p>
<h2 id="🌱-project1의-시작">🌱 project1의 시작</h2>
<h4 id="기간">기간</h4>
<ul>
<li>2024.12.11~ 2024.12.30</li>
<li>총 20일<h4 id="구성원">구성원</h4>
</li>
<li>김연준, 얀진람, 임진우, 최윤석, 한예나</li>
</ul>
<hr>
<h2 id="💡-meeting">💡 Meeting</h2>
<h3 id="팀-이름-정하기">팀 이름 정하기</h3>
<ul>
<li>푸룬(Puleus) : 우리의 프로젝트 새싹처럼 푸릇푸릇 자라나자는 의미<h3 id="임무-분담">임무 분담</h3>
<table>
<thead>
<tr>
<th>이름</th>
<th>분야</th>
<th>페이지</th>
<th>세부</th>
<th>GitHub</th>
</tr>
</thead>
<tbody><tr>
<td>김연준</td>
<td>FE&amp;BE</td>
<td>상품 목록 및 리뷰</td>
<td>제품 DB 수집, 제품 상세</td>
<td><a href="https://github.com/digimonMania">GitHub</a></td>
</tr>
<tr>
<td>얀진람</td>
<td>FE&amp;BE</td>
<td>회원가입 및 로그인</td>
<td>ERD구성, 회원가입, 탈퇴, 로그인</td>
<td><a href="https://github.com/yanjinlkham9">GitHub</a></td>
</tr>
<tr>
<td>임진우</td>
<td>FE&amp;BE</td>
<td>메인 및 검색</td>
<td>광고, 이메일 이벤트, 검색</td>
<td><a href="https://github.com/YENANOAH">GitHub</a></td>
</tr>
<tr>
<td>최윤석</td>
<td>FE&amp;BE</td>
<td>자유게시판</td>
<td>노션 관리(회의록, API 명세서), 호스팅 배포(AWS)</td>
<td><a href="https://github.com/projectCHOI">GitHub</a></td>
</tr>
<tr>
<td>한예나</td>
<td>FE&amp;BE</td>
<td>마이페이지</td>
<td>자기가 쓴 리뷰, 글/댓글, 좋아요</td>
<td><a href="https://github.com/imujinu">GitHub</a></td>
</tr>
</tbody></table>
</li>
</ul>
<hr>
<h4 id="웹-구성-개발환경">웹 구성 개발환경</h4>
<p>IDE : Visual Studio
Framework : Express.js
ORM = Sequelize</p>
<h4 id="기술">기술</h4>
<p>Server : AWS EC2
DataBase : Mysql</p>
<h2 id="✨-기능-회의-내용">✨ 기능 회의 내용</h2>
<ul>
<li>핵심 내용 : 사용자에게 시각적인 효과와 다양한 정보를 제공할 수 있는 웹페이지를 구상 하는 것이 목표<h3 id="서비스-페이지-기능">서비스 페이지 기능</h3>
</li>
<li>주요 기능 : 사용자 정보 제공과 사용자간 상호작용 목적</li>
</ul>
<hr>
<h3 id="💻-1차-아이디어">💻 1차 아이디어</h3>
<ol>
<li><strong>김연준</strong> : API 생성 웹 페이지
내용 : ChatGPT를 사용해 챗봇 UI를 제작하여 사용자 맞춤 API를 생성하는 웹 페이지 구현</li>
<li><strong>임진우</strong> : 부동산 정보 제공 웹 페이지
내용 : LH, SH 등의 부동산 정보를 사용자에게 제공하는 웹 페이지 구현</li>
<li><strong>얀진람</strong> : 화장품 추천 웹 페이지
내용 : 사용자의 피부 상태를 입력하면 최적의 제품을 제공하는 웹 페이지 구현</li>
<li><strong>최윤석</strong> : 비플페이(제로페이) 가맹점 모음 웹 페이지
내용 : 창동역 인근 식당 중 비플페이(제로페이)가 사용 가능한 식당을 나타내는 웹 페이지 구현</li>
<li><strong>한예나</strong> : 컨셉 커뮤니티 웹 페이지
내용 : 특정 컨셉을 주제로 모인 사람들이 의견을 공유 할 수 있는 웹 페이지 구현<h3 id="1차-주제-회의-결과">1차 주제 회의 결과</h3>
<h4 id="기업가치-리뷰-웹-페이지-구현">기업가치 리뷰 웹 페이지 구현</h4>
사용자가 코스피 100의 기업을 대상으로 특정 기업을 선택 시 기업 가치 리뷰 웹 페이지 구현해보자.<h4 id="서비스-페이지-기능-1">서비스 페이지 기능</h4>
</li>
</ol>
<ul>
<li><input disabled="" type="checkbox"> 100의 기업을 &quot;조회&quot;</li>
<li><input disabled="" type="checkbox"> 기업의 간략한 소개</li>
<li><input disabled="" type="checkbox"> 과거 주가, 거래량 그래프 시각화</li>
<li><input disabled="" type="checkbox"> 현재 가격, 최고가, 최저가 최신 정보 업데이트</li>
<li><input disabled="" type="checkbox"> 기업가치에 대한 의견을 나눌 수 있는 방명록(댓글 or 사진)<h4 id="데이터">데이터</h4>
</li>
<li>코스피 100의 주식 데이터</li>
</ul>
<hr>
<h2 id="📘-meeting">📘 Meeting</h2>
<h3 id="트러블-슈팅">트러블 슈팅</h3>
<h4 id="🔥안건">🔥안건</h4>
<ul>
<li>주제 회의 결과 학습한 내용을 100% 활용하지 못하는 한계점 지적 받았다.</li>
</ul>
<ol>
<li>배운 것의 일부분만 사용하는건 project 의의에 맞지 않다.</li>
<li>평가자(동료,리더)의 공감을 얻기에 주제가 부적합하다.<h4 id="🔎-해결방안">🔎 해결방안</h4>
주제 회의 결과 학습한 내용을 100% 활용하지 못하는 한계점 지적 받았다. 결과적으로 사용 경험이 많은 편의점의 제품을 비교 정보제공, 사용자간의 의견을 교류하는 웹 페이지를 제작하는 것으로 결정</li>
</ol>
<hr>
<h3 id="💻-2차-주제-회의-결과">💻 2차 주제 회의 결과</h3>
<h4 id="편의점--제품-리뷰-웹-페이지-구현">편의점  제품 리뷰 웹 페이지 구현</h4>
<p>편의점 업체 소수를 지정해 [도시락, 샐러드, 샌드위치, 김밥] 4개 품목에 대한 가격 비교와 할인 정보 제공 웹 페이지 구현</p>
<h4 id="서비스-페이지-기능-2">서비스 페이지 기능</h4>
<ul>
<li><input disabled="" type="checkbox"> 편의점의  대상 품목 &quot;조회&quot;</li>
<li><input disabled="" type="checkbox"> 대상 품목의 세부 정보(가격, <em>사진</em>, <em>이름</em>) 제공</li>
<li><input disabled="" type="checkbox"> 12월 할인 품목 한 눈에 시각화</li>
<li><input disabled="" type="checkbox"> 공통 제품 간 가격 비교</li>
<li><input disabled="" type="checkbox"> 로그인 기능(게시글 업로드 허가)</li>
<li><input disabled="" type="checkbox"> 제품에 대한 의견을 나눌 수 있는 방명록(댓글 or 사진) 업로드 기능(로그인)<h4 id="데이터-1">데이터</h4>
</li>
<li>편의점 대상 품목 수집</li>
</ul>
<hr>
<h2 id="⚙️다음-회의-준비">⚙️다음 회의 준비</h2>
<ul>
<li>코딩을 위한 협업 규칙</li>
<li>웹 페이지 디자인 회의</li>
<li>사용자의 상호작용(클릭, 호버, 입력) 위치와 </li>
<li>각자 웹 페이지 그림 그려 올 것!</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[실습 - Relationship의 실습]]></title>
            <link>https://velog.io/@yoonsuk_choi/%EC%8B%A4%EC%8A%B5-Relationship%EC%9D%98-%EC%8B%A4%EC%8A%B5</link>
            <guid>https://velog.io/@yoonsuk_choi/%EC%8B%A4%EC%8A%B5-Relationship%EC%9D%98-%EC%8B%A4%EC%8A%B5</guid>
            <pubDate>Thu, 12 Dec 2024 15:20:30 GMT</pubDate>
            <description><![CDATA[<h2 id="mvc와-relationship-실습">MVC와 Relationship 실습</h2>
<p><img src="https://velog.velcdn.com/images/yoonsuk_choi/post/9c6f1bac-eef5-40f0-9a92-6b65dfa26ba5/image.JPG" alt=""></p>
<h3 id="relationship의-내용">Relationship의 내용</h3>
<h3 id="관계-정의">관계 정의</h3>
<ol>
<li><strong>Player - Profile 관계</strong>: 1:1 관계를 통해 선수와 그의 세부 정보를 연결. 선수 삭제 시 프로필도 함께 삭제됨.</li>
<li><strong>Team - Player 관계</strong>: 1:N 관계를 통해 팀과 소속 선수들을 연결.</li>
<li><strong>Team - Game 관계</strong>: M:N 관계를 통해 중개 테이블인 <code>TeamGame</code>을 사용해 팀과 경기의 관계를 정의.</li>
</ol>
<h3 id="sequelize를-활용한-모델-관계-설정">Sequelize를 활용한 모델 관계 설정</h3>
<ul>
<li><code>Player</code> 모델과 <code>Profile</code> 모델 간 관계 정의:<pre><code class="language-javascript">PlayerModel.hasOne(ProfileModel, {
  onDelete: &quot;CASCADE&quot;,
  onUpdate: &quot;CASCADE&quot;,
  foreignKey: &quot;player_id&quot;,
});
ProfileModel.belongsTo(PlayerModel, {
  foreignKey: &quot;player_id&quot;,
});</code></pre>
</li>
<li><code>Team</code> 모델과 <code>Player</code> 모델 간 관계 정의:<pre><code class="language-javascript">TeamModel.hasMany(PlayerModel, {
  foreignKey: &quot;teamid&quot;,
  sourceKey: &quot;team_id&quot;,
});
PlayerModel.belongsTo(TeamModel, {
  foreignKey: &quot;teamid&quot;,
  targetKey: &quot;team_id&quot;,
});</code></pre>
</li>
<li><code>Team</code> 모델과 <code>Game</code> 모델 간 M:N 관계 정의:<pre><code class="language-javascript">TeamModel.belongsToMany(GameModel, {
  through: TeamGameModel,
  foreignKey: &quot;team_id&quot;,
});
GameModel.belongsToMany(TeamModel, {
  through: TeamGameModel,
  foreignKey: &quot;game_id&quot;,
});</code></pre>
</li>
</ul>
<hr>
<h2 id="실습결과">실습결과</h2>
<h3 id="성공적으로-구현된-기능">성공적으로 구현된 기능</h3>
<ol>
<li><p><strong>Player CRUD</strong>:</p>
<ul>
<li>전체 선수 조회</li>
<li>특정 선수와 프로필 정보 조회 (JOIN 활용)</li>
<li>새로운 선수 추가</li>
<li>특정 선수의 팀 변경</li>
<li>특정 선수 삭제</li>
</ul>
</li>
<li><p><strong>Team 검색 및 정렬</strong>:</p>
<ul>
<li>이름순 정렬</li>
<li>키워드 검색</li>
</ul>
</li>
<li><p><strong>Team - Player JOIN</strong>:</p>
<ul>
<li>특정 팀의 선수 정보 조회</li>
</ul>
</li>
<li><p><strong>Database 연결 성공</strong>:</p>
<ul>
<li>Sequelize ORM을 통해 MySQL 데이터베이스와 성공적으로 통신.</li>
</ul>
</li>
</ol>
<hr>
<h2 id="폴더-구조와-코드의-역할">폴더 구조와 코드의 역할</h2>
<h3 id="폴더-구조">폴더 구조</h3>
<pre><code>15_MCV-sequelize
├── config/
│   ├── config.js
├── controller/
│   ├── Cmain.js
├── models
│   ├── Game.js
│   ├── Player.js
│   ├── Team.js
│   ├── TeamGame.js
│   ├── index.js
│   ├── profile.js
├── routes
│    ── index.js
├── node_modules/
│   ├── 다양한 파일
├── views/
│   ├── index.ejs
├── app.js
├── init.sql
├── package-lock.json
└── package.json</code></pre><h3 id="코드별-역할">코드별 역할</h3>
<h4 id="configconfigjs"><code>config/config.js</code></h4>
<ul>
<li>환경변수에서 데이터베이스 설정을 가져와 Sequelize에 전달.</li>
</ul>
<h4 id="controllercmainjs"><code>controller/Cmain.js</code></h4>
<ul>
<li><strong>주요 역할</strong>: 요청 처리 및 데이터베이스와 상호작용.<ul>
<li>선수 CRUD.</li>
<li>팀 검색, 정렬 및 선수 조회.</li>
<li>복잡한 조건 검색 및 정렬 처리.</li>
</ul>
</li>
</ul>
<h4 id="modelsindexjs"><code>models/index.js</code></h4>
<ul>
<li><strong>주요 역할</strong>: Sequelize 초기화 및 모델 관계 설정.</li>
</ul>
<h4 id="modelsjs"><code>models/*.js</code></h4>
<ul>
<li>각 데이터베이스 테이블을 나타내는 모델 정의.</li>
<li><code>Game.js</code>, <code>Player.js</code>, <code>Team.js</code>, <code>TeamGame.js</code>, <code>profile.js</code> 모델은 테이블 구조 및 데이터 타입을 명시.</li>
</ul>
<h4 id="routesindexjs"><code>routes/index.js</code></h4>
<ul>
<li>요청 URL에 따라 컨트롤러와 라우트 매핑.</li>
</ul>
<h4 id="viewsindexejs"><code>views/index.ejs</code></h4>
<ul>
<li>HTML 템플릿으로 사용자 인터페이스 제공.</li>
</ul>
<h4 id="appjs"><code>app.js</code></h4>
<ul>
<li>Express 서버 설정 및 Sequelize와 연결.</li>
<li>전체 애플리케이션의 진입점.</li>
</ul>
<hr>
<h2 id="코드-내용">코드 내용</h2>
<p><a href="https://github.com/projectCHOI/SeSAC-codingOn_WebFull-Stack/tree/main/16_Relationship">GitHub 링크 바로가기</a></p>
<h4 id="appjs-1"><code>app.js</code></h4>
<pre><code>const express = require(&quot;express&quot;);
const app = express();
const PORT = 8080;
const { sequelize } = require(&quot;./models&quot;);
// db={sequelize:~~~~, Sequelize: ~~~~}
// const {sequelize} =db;

// set middleware
app.set(&quot;view engine&quot;, &quot;ejs&quot;);
app.use(express.urlencoded({ extended: false }));
app.use(express.json());

// router 설정
const indexRouter = require(&quot;./routes&quot;);
app.use(&quot;/&quot;, indexRouter);

sequelize
  .sync({ force: false })
  .then(() =&gt; {
    console.log(&quot;db connection success!&quot;);
    app.listen(PORT, () =&gt; {
      console.log(`http://localhost:${PORT}`);
    });
  })
  .catch((err) =&gt; {
    console.log(&quot;db connection Err!&quot;);
    console.log(err);
  });</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[학습 - Relationship의 학습]]></title>
            <link>https://velog.io/@yoonsuk_choi/%ED%95%99%EC%8A%B5-Relationship%EC%9D%98-%ED%95%99%EC%8A%B5</link>
            <guid>https://velog.io/@yoonsuk_choi/%ED%95%99%EC%8A%B5-Relationship%EC%9D%98-%ED%95%99%EC%8A%B5</guid>
            <pubDate>Thu, 12 Dec 2024 14:56:19 GMT</pubDate>
            <description><![CDATA[<h3 id="relationship이란">Relationship이란?</h3>
<p><img src="https://velog.velcdn.com/images/yoonsuk_choi/post/1205049b-adf2-4000-bd3c-0798d8fc2984/image.JPG" alt=""></p>
<ul>
<li>Relationship은 데이터베이스에서 테이블 간의 연관성을 정의한 개념</li>
<li>테이블 간의 관계를 설정하여 데이터의 일관성을 유지하고 효율적으로 데이터를 처리할 수 있게 한다.</li>
<li>관계형 데이터베이스에서는 주로 외래 키(Foreign Key)를 사용하여 관계를 설정한다.</li>
</ul>
<h3 id="join-종류와-생성방법">JOIN 종류와 생성방법</h3>
<h4 id="1대-1-관계-one-to-one">1대 1 관계 (One-to-One)</h4>
<ul>
<li>각 행이 서로 다른 테이블의 행 하나와만 연결되는 관계.</li>
<li>예시: 사용자 테이블(User)과 사용자 프로필 테이블(Profile)</li>
</ul>
<p><strong>설정 방법:</strong></p>
<ol>
<li>한 테이블의 기본 키(Primary Key)를 다른 테이블의 외래 키(Foreign Key)로 설정.</li>
</ol>
<h4 id="1대-다수-관계-one-to-many">1대 다수 관계 (One-to-Many)</h4>
<ul>
<li>한 테이블의 한 행이 다른 테이블의 여러 행과 연결되는 관계.</li>
<li>예시: 카테고리 테이블(Category)와 제품 테이블(Product)</li>
</ul>
<p><strong>설정 방법:</strong></p>
<ol>
<li>한 테이블의 기본 키를 다른 테이블의 외래 키로 설정.</li>
</ol>
<h4 id="다수-대-다수-관계-many-to-many">다수 대 다수 관계 (Many-to-Many)</h4>
<ul>
<li>여러 행이 다른 테이블의 여러 행과 연결되는 관계.</li>
<li>예시: 학생 테이블(Student)과 수업 테이블(Class)</li>
</ul>
<p><strong>설정 방법:</strong></p>
<ol>
<li>중간 테이블(Bridge Table)을 생성하여 두 테이블 간의 관계를 설정.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[# 학습 - JOIN의 학습]]></title>
            <link>https://velog.io/@yoonsuk_choi/%ED%95%99%EC%8A%B5-JOIN%EC%9D%98-%ED%95%99%EC%8A%B5</link>
            <guid>https://velog.io/@yoonsuk_choi/%ED%95%99%EC%8A%B5-JOIN%EC%9D%98-%ED%95%99%EC%8A%B5</guid>
            <pubDate>Thu, 12 Dec 2024 14:54:08 GMT</pubDate>
            <description><![CDATA[<h3 id="join-이란">JOIN 이란?</h3>
<p><img src="https://velog.velcdn.com/images/yoonsuk_choi/post/554eb9bf-60e3-4e07-a611-36b6a900d9b7/image.JPG" alt=""></p>
<p>JOIN은 두 개 이상의 테이블을 연결하여 데이터를 검색하는 SQL의 기능입니다. 이를 통해 관계형 데이터베이스의 여러 테이블에 분산된 데이터를 통합적으로 조회할 수 있습니다.</p>
<h3 id="join-종류">JOIN 종류</h3>
<ol>
<li><p><strong>INNER JOIN</strong></p>
<ul>
<li>공통된 값을 가진 행만 반환</li>
</ul>
</li>
<li><p><strong>LEFT JOIN (또는 LEFT OUTER JOIN)</strong></p>
<ul>
<li>왼쪽 테이블의 모든 데이터를 반환하며, 오른쪽 테이블에 일치하는 데이터가 없으면 NULL로 표시</li>
</ul>
</li>
<li><p><strong>RIGHT JOIN (또는 RIGHT OUTER JOIN)</strong></p>
<ul>
<li>오른쪽 테이블의 모든 데이터를 반환하며, 왼쪽 테이블에 일치하는 데이터가 없으면 NULL로 표시</li>
</ul>
</li>
<li><p><strong>FULL JOIN (또는 FULL OUTER JOIN)</strong></p>
<ul>
<li>양쪽 테이블의 모든 데이터를 반환하며, 일치하지 않는 데이터는 NULL로 표시</li>
</ul>
</li>
</ol>
<h3 id="join-예시">JOIN 예시</h3>
<ol>
<li><p><strong>INNER JOIN</strong></p>
<pre><code class="language-sql">SELECT employees.name, departments.department_name
FROM employees
INNER JOIN departments ON employees.department_id = departments.id;</code></pre>
</li>
<li><p><strong>LEFT JOIN</strong></p>
<pre><code class="language-sql">SELECT employees.name, departments.department_name
FROM employees
LEFT JOIN departments ON employees.department_id = departments.id;</code></pre>
</li>
<li><p><strong>RIGHT JOIN</strong></p>
<pre><code class="language-sql">SELECT employees.name, departments.department_name
FROM employees
RIGHT JOIN departments ON employees.department_id = departments.id;</code></pre>
</li>
<li><p><strong>FULL JOIN</strong></p>
<pre><code class="language-sql">SELECT employees.name, departments.department_name
FROM employees
FULL JOIN departments ON employees.department_id = departments.id;</code></pre>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[실습 - MCV-sequelize의 실습]]></title>
            <link>https://velog.io/@yoonsuk_choi/%EC%8B%A4%EC%8A%B5-MCV-sequelize%EC%9D%98-%EC%8B%A4%EC%8A%B5</link>
            <guid>https://velog.io/@yoonsuk_choi/%EC%8B%A4%EC%8A%B5-MCV-sequelize%EC%9D%98-%EC%8B%A4%EC%8A%B5</guid>
            <pubDate>Thu, 12 Dec 2024 14:40:28 GMT</pubDate>
            <description><![CDATA[<h3 id="mvc와-mysql의-연결-내용">MVC와 MySQL의 연결 내용</h3>
<p><img src="https://velog.velcdn.com/images/yoonsuk_choi/post/07bddee9-dae3-469e-958f-e0a4258e934d/image.JPG" alt=""></p>
<ul>
<li>MVC 패턴을 활용하여 Sequelize ORM과 MySQL 데이터베이스를 연결한 프로젝트<h3 id="실습-결과">실습 결과</h3>
</li>
<li>회원가입, 로그인, 회원정보 수정 및 삭제 기능을 정상적으로 구현.</li>
<li>데이터베이스와의 연결을 통해 데이터를 안전하게 CRUD(Create, Read, Update, Delete) 처리.</li>
<li>View 단에서 EJS 템플릿을 활용하여 동적 웹 페이지 생성.<h4 id="mvc와-mysql의-연결-내용-1">MVC와 MySQL의 연결 내용</h4>
</li>
<li>MySQL 데이터베이스와의 연동<ul>
<li>Sequelize의 <code>sync</code> 메소드를 통해 데이터베이스 테이블을 자동으로 생성하거나 업데이트.</li>
</ul>
</li>
</ul>
<h3 id="폴더-구조와-코드의-역할">폴더 구조와 코드의 역할</h3>
<pre><code>15_MCV-sequelize
├── config/
│   ├── config.js   // 환경 변수로부터 DB 연결 정보를 가져오는 설정 파일.
│   ├── config.json // 기본 DB 설정 정보 (dotenv를 통해 대체).
├── controller/
│   ├── Cuser.js    // 사용자 관련 로직 처리 컨트롤러.
├── models
│   ├── User.js     // 사용자 모델 정의.
│   ├── index.js    // Sequelize 초기화 및 모델 등록.
│   ├── 기존 User.js // MySQL 직접 쿼리 작성했던 코드.
├── routes
│    ── user.js      // 사용자 관련 라우트 정의.
├── node_modules/   // 프로젝트 의존성 파일.
├── views/
│   │   ├── include/
│   │   │   ├── include.ejs // 공통 헤더 파일.
│   ├── 404.ejs     // 404 에러 페이지.
│   ├── index.ejs   // 메인 페이지.
│   ├── profile.ejs // 회원 정보 페이지.
│   ├── signin.ejs  // 로그인 페이지.
│   ├── signup.ejs  // 회원가입 페이지.
├── .env            // 환경 변수 파일.
├── app.js          // Express 애플리케이션 초기화 및 설정.
├── package-lock.json
└── package.json    // 프로젝트 메타 정보.</code></pre><h2 id="코드별-내용">코드별 내용</h2>
<p><a href="https://github.com/projectCHOI/SeSAC-codingOn_WebFull-Stack/tree/main/15_MCV-sequelize2">GitHub 링크 바로가기</a></p>
<h3 id="1-configjs">1. config.js</h3>
<ul>
<li>환경 변수에서 데이터베이스 설정 정보를 가져와 Sequelize의 설정을 제공<h3 id="2-cuserjs-controller">2. Cuser.js (Controller)</h3>
</li>
<li>사용자 관련 요청을 처리하는 컨트롤러로, View와 Model 사이의 비즈니스 로직을 수</li>
<li>주요 기능:</li>
</ul>
<p>1.회원가입: <code>User.create()</code>
2.로그인: <code>User.findOne()</code>
3.회원정보 수정: <code>User.update()</code>
4.회원 탈퇴: <code>User.destroy()</code></p>
<h3 id="3-userjs-model">3. User.js (Model)</h3>
<ul>
<li>Sequelize를 통해 정의된 사용자 모델로, 테이블 컬럼과 속성 정의<h3 id="4-userjs-기존">4. User.js (기존)</h3>
</li>
<li>직접 SQL 쿼리를 작성했던 코드를 포함합니다. Sequelize로 대체<h3 id="5-routes">5. Routes</h3>
</li>
<li>라우트 설정 파일로, 요청 URL과 컨트롤러를 연결<h3 id="6-ejs-views">6. EJS Views</h3>
각 페이지의 템플릿을 정의하며, 동적 데이터 렌더링을 지원합니다.</li>
<li><code>index.ejs</code>: 메인 페이지.</li>
<li><code>signup.ejs</code>: 회원가입 폼.</li>
<li><code>signin.ejs</code>: 로그인 폼.</li>
<li><code>profile.ejs</code>: 회원정보 수정/삭제.</li>
<li><code>404.ejs</code>: 에러 페이지.<h3 id="7-appjs">7. app.js</h3>
Express 애플리케이션의 초기 설정 및 실행 파일.</li>
<li>미들웨어 구성 (body-parser, 라우트 등).</li>
<li>Sequelize <code>sync</code>를 통해 DB 연결 확인.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[학습 - MCV-sequelize의 학습]]></title>
            <link>https://velog.io/@yoonsuk_choi/%ED%95%99%EC%8A%B5-MCV-sequelize%EC%9D%98-%ED%95%99%EC%8A%B5</link>
            <guid>https://velog.io/@yoonsuk_choi/%ED%95%99%EC%8A%B5-MCV-sequelize%EC%9D%98-%ED%95%99%EC%8A%B5</guid>
            <pubDate>Thu, 12 Dec 2024 14:08:14 GMT</pubDate>
            <description><![CDATA[<h3 id="sequelize란">sequelize란?</h3>
<p><img src="https://velog.velcdn.com/images/yoonsuk_choi/post/e1f80663-c282-4912-99d9-e26682297b9c/image.JPG" alt=""></p>
<ul>
<li>Sequelize는 Node.js에서 사용하는 ORM(Object-Relational Mapping) 라이브러리</li>
<li>ORM은 관계형 데이터베이스를 객체지향적으로 다룰 수 있게 해주는 기술이다. SQL 쿼리를 직접 작성하지 않고도 JavaScript 객체를 이용하여 데이터베이스와 상호작용할 수 있게 해준다.<h3 id="sequelize-학습">sequelize 학습</h3>
<img src="https://velog.velcdn.com/images/yoonsuk_choi/post/a333b758-9fe6-43fb-9cfe-eaacdbcfe15d/image.jpg" alt=""></li>
</ul>
<h4 id="model">Model</h4>
<ul>
<li>Model은 Sequelize ORM을 사용하여 데이터베이스와의 연결을 설정하고, 데이터베이스의 테이블을 정의<h4 id="view">View</h4>
</li>
<li>프론트엔드와 관련이 있으며, 사용자에게 데이터를 어떻게 표시할지를 결정<h4 id="controller">Controller</h4>
</li>
<li>클라이언트의 요청을 처리하고, 적절한 모델을 호출하여 데이터를 가져오거나 수정<h3 id="전체적인-흐름">전체적인 흐름</h3>
</li>
</ul>
<ol>
<li>사용자가 웹 애플리케이션에 요청을 전달</li>
<li>Controller가 요청을 받아 처리</li>
<li>Controller는 Model을 호출하여 데이터베이스에서 필요한 데이터를 가져온다.</li>
<li>가져온 데이터를 View로 전달하여 사용자에게 표시<h3 id="결론">결론</h3>
</li>
</ol>
<p>-MCV-Sequelize는 모델-뷰-컨트롤러 패턴과 Sequelize ORM을 결합한 구조로, 데이터베이스와의 상호작용을 효율적으로 처리하면서 깔끔한 아키텍처를 유지, 관리 할 수 있게 해준다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[실습 - MySQL release의 실습]]></title>
            <link>https://velog.io/@yoonsuk_choi/%EC%8B%A4%EC%8A%B5-MySQL-release%EC%9D%98-%EC%8B%A4%EC%8A%B5</link>
            <guid>https://velog.io/@yoonsuk_choi/%EC%8B%A4%EC%8A%B5-MySQL-release%EC%9D%98-%EC%8B%A4%EC%8A%B5</guid>
            <pubDate>Wed, 11 Dec 2024 01:45:36 GMT</pubDate>
            <description><![CDATA[<h1 id="6주차_mysql_release-실습">6주차_MySQL_release 실습</h1>
<p><img src="https://velog.velcdn.com/images/yoonsuk_choi/post/c432e7a2-42b0-4196-87c2-194250f818fb/image.JPG" alt=""></p>
<h2 id="mvc와-mysql의-연결-내용"><strong>MVC와 MySQL의 연결 내용</strong></h2>
<ul>
<li>MVC(Model-View-Controller) 패턴을 기반으로 Express.js와 MySQL을 연결하여 회원가입, 로그인, 프로필 수정 및 삭제 기능 구현</li>
</ul>
<hr>
<h3 id="실습-결과"><strong>실습 결과</strong></h3>
<ol>
<li><p><strong>회원가입</strong>  </p>
<ul>
<li>유효한 ID, 비밀번호, 이름 입력 시 데이터베이스에 성공적으로 저장되고, JSON 응답을 통해 성공 여부 반환.  </li>
<li>이미 존재하는 ID로 가입을 시도하면 실패 응답 처리.</li>
</ul>
</li>
<li><p><strong>로그인</strong>  </p>
<ul>
<li>유효한 자격증명(ID 및 비밀번호)으로 로그인하면 사용자 정보를 반환하며, 프로필 페이지로 리디렉션.  </li>
<li>잘못된 자격증명으로 로그인 시도 시 실패 응답 처리.</li>
</ul>
</li>
<li><p><strong>회원정보 수정 및 탈퇴</strong>  </p>
<ul>
<li>이름 및 비밀번호 수정 시 데이터베이스에 반영되며, 수정 성공 여부 반환.  </li>
<li>탈퇴 시 해당 사용자가 데이터베이스에서 삭제되고, 성공 여부 반환.</li>
</ul>
</li>
<li><p><strong>뷰 렌더링 및 페이지 이동</strong>  </p>
<ul>
<li>각각의 뷰 파일이 요청에 따라 정상적으로 렌더링되며, 경로에 따른 페이지 이동이 원활히 작동.</li>
</ul>
<hr>
<h3 id="폴더-구조와-코드의-역할"><strong>폴더 구조와 코드의 역할</strong></h3>
<pre><code>MVC_MySQL_release
├── controller/
│   ├── Cuser.js
├── model/
│   ├── User.js
├── node_modules/
│   ├── 다양한 파일
├── routes/
│   ├── user.js
├── views/
│   │   ├── include/
│   │   │   ├── include.ejs
│   ├── 404.ejs
│   ├── index.ejs
│   ├── profile.ejs
│   ├── signin.ejs
│   ├── signup.ejs
├── .gitignore
├── app.js
├── init.sql
├── package-lock.json
└── package.json</code></pre></li>
</ol>
<ul>
<li><p><strong>controller/</strong>  </p>
<ul>
<li><code>Cuser.js 주요 기능</code> : 회원가입 처리, 로그인 처리, 회원 정보 수정, 회원 탈퇴.</li>
</ul>
</li>
<li><p><strong>model/</strong>  </p>
<ul>
<li><code>User.js 주요 기능</code> : 주요 기능: 유저 데이터 삽입, 조회, 수정, 삭제.</li>
</ul>
</li>
<li><p><strong>routes/</strong></p>
<ul>
<li><code>user.js 주요 기능</code> : 사용자 관련 요청 경로(<code>/user</code>)를 정의하고 컨트롤러와 연결</li>
<li>주요 라우트 : 회원가입(<code>/signup</code>), 로그인(<code>/signin</code>), 프로필 수정(<code>/profile/edit</code>), 프로필 삭제(<code>/profile/delete</code>).</li>
</ul>
</li>
<li><p><strong>views/</strong>  </p>
<ul>
<li><code>index.ejs</code>: 회원가입 및 로그인 페이지로 이동할 수 있는 링크 제공  </li>
<li><code>signup.ejs</code>: 회원가입 페이지, 사용자로부터 ID, 비밀번호, 이름을 입력  </li>
<li><code>signin.ejs</code>: 로그인 페이지, 사용자로부터 ID와 비밀번호를 입력  </li>
<li><code>profile.ejs</code>: 로그인 후 유저 정보를 수정, 탈퇴 페이지  </li>
<li><code>404.ejs</code>: 에러 페이지입니다.</li>
</ul>
</li>
</ul>
<hr>
<h3 id="코드별-내용"><strong>코드별 내용</strong></h3>
<h4 id="controllercuserjs"><strong><code>controller/Cuser.js</code></strong></h4>
<pre><code>const User = require(&quot;../model/User&quot;);

// 회원가입 처리
exports.signup = (req, res) =&gt; {
  const { userid, name, pw } = req.body;
  User.signup({ userid, name, pw }, (result) =&gt; {
    if (result) {
      res.status(201).json({ success: true, message: &quot;회원가입 성공&quot; });
    } else {
      res.status(500).json({ success: false, message: &quot;회원가입 실패&quot; });
    }
  });
};

// 로그인 처리
exports.signin = (req, res) =&gt; {
  const { userid, pw } = req.body;
  User.signin({ userid, pw }, (user) =&gt; {
    if (user) {
      res.status(200).json({ success: true, user });
    } else {
      res.status(401).json({ success: false, message: &quot;로그인 실패&quot; });
    }
  });
};

// 회원정보 수정
exports.editProfile = (req, res) =&gt; {
  const { id, name, pw } = req.body;
  User.update({ id, name, pw }, (result) =&gt; {
    if (result) {
      res.status(200).json({ success: true, message: &quot;정보 수정 성공&quot; });
    } else {
      res.status(500).json({ success: false, message: &quot;정보 수정 실패&quot; });
    }
  });
};

// 회원탈퇴 처리
exports.deleteProfile = (req, res) =&gt; {
  const { id } = req.body;
  User.delete(id, (result) =&gt; {
    if (result) {
      res.status(200).json({ success: true, message: &quot;회원 탈퇴 성공&quot; });
    } else {
      res.status(500).json({ success: false, message: &quot;회원 탈퇴 실패&quot; });
    }
  });
};
</code></pre><h4 id="modeluserjs"><strong><code>model/User.js</code></strong></h4>
<pre><code>const mysql = require(&quot;mysql2&quot;);

const connection = mysql.createConnection({
  host: &quot;localhost&quot;,
  user: &quot;root&quot;,
  password: &quot;3144&quot;,
  database: &quot;sesac&quot;,
});

connection.connect();

// 회원가입
exports.signup = (data, callback) =&gt; {
  const query = &quot;INSERT INTO user (userid, name, pw) VALUES (?, ?, ?)&quot;;
  connection.query(query, [data.userid, data.name, data.pw], (err, result) =&gt; {
    if (err) {
      console.error(err);
      return callback(false);
    }
    callback(true);
  });
};

// 로그인
exports.signin = (data, callback) =&gt; {
  const query = &quot;SELECT * FROM user WHERE userid = ? AND pw = ?&quot;;
  connection.query(query, [data.userid, data.pw], (err, results) =&gt; {
    if (err) {
      console.error(err);
      return callback(null);
    }
    callback(results[0]);
  });
};

// 회원정보 수정
exports.update = (data, callback) =&gt; {
  const query = &quot;UPDATE user SET name = ?, pw = ? WHERE id = ?&quot;;
  connection.query(query, [data.name, data.pw, data.id], (err, result) =&gt; {
    if (err) {
      console.error(err);
      return callback(false);
    }
    callback(true);
  });
};

// 회원탈퇴
exports.delete = (id, callback) =&gt; {
  const query = &quot;DELETE FROM user WHERE id = ?&quot;;
  connection.query(query, [id], (err, result) =&gt; {
    if (err) {
      console.error(err);
      return callback(false);
    }
    callback(true);
  });
};
</code></pre><h4 id="routesuserjs"><strong><code>routes/user.js</code></strong></h4>
<pre><code>const express = require(&quot;express&quot;);
const router = express.Router();
const Cuser = require(&quot;../controller/Cuser&quot;);

// 회원가입 페이지 렌더링 (GET /signup)
router.get(&quot;/signup&quot;, (req, res) =&gt; {
  res.render(&quot;signup&quot;, { title: &quot;회원가입&quot; });
});

// 회원가입 처리 (POST /signup)
router.post(&quot;/signup&quot;, Cuser.signup);

// 로그인 페이지 렌더링 (GET /signin)
router.get(&quot;/signin&quot;, (req, res) =&gt; {
  res.render(&quot;signin&quot;, { title: &quot;로그인&quot; });
});

// 로그인 처리 (POST /signin)
router.post(&quot;/signin&quot;, Cuser.signin);

// 회원정보 수정 (POST /profile/edit)
router.post(&quot;/profile/edit&quot;, Cuser.editProfile);

// 회원탈퇴 처리 (DELETE /profile/delete)
router.delete(&quot;/profile/delete&quot;, Cuser.deleteProfile);

module.exports = router;
</code></pre><h3 id="뷰-파일-views"><strong>뷰 파일 (<code>views/</code>)</strong></h3>
<h4 id="404ejs"><strong><code>404.ejs</code></strong></h4>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot;&gt;
  &lt;%- include(&#39;include/include&#39;, { title: &#39;404 Error&#39; })%&gt;
  &lt;body&gt;
    &lt;h1&gt;🚨404 Error&lt;/h1&gt;
    &lt;p&gt;죄송합니다. 해당 URL은 접근할 수 없는 주소입니다.&lt;/p&gt;
    &lt;!-- TODO: href 속성값 설정하기 --&gt;
    &lt;a href=&quot;/&quot;&gt;HOME&lt;/a&gt;
  &lt;/body&gt;
&lt;/html&gt;
</code></pre><h4 id="indexejs"><strong><code>index.ejs</code></strong></h4>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot;&gt;
  &lt;%- include(&#39;include/include&#39;, { title: &#39;실습 | 회원가입/로그인 구현&#39; })%&gt;
  &lt;body&gt;
    &lt;h1&gt;실습. 회원가입과 로그인 DB 연동&lt;/h1&gt;
    &lt;!-- TODO: href 속성값 설정하기 --&gt;
    &lt;a href=&quot;/user/signup&quot;&gt;회원가입&lt;/a&gt;
    &lt;a href=&quot;/user/signin&quot;&gt;로그인&lt;/a&gt;
  &lt;/body&gt;
&lt;/html&gt;
</code></pre><h4 id="profileejs"><strong><code>profile.ejs</code></strong></h4>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot;&gt;
  &lt;%- include(&#39;include/include&#39;, { title: &#39;회원 정보&#39; })%&gt;
  &lt;body&gt;
    &lt;h1&gt;회원 정보&lt;/h1&gt;
    &lt;!-- TODO: href 속성값 설정하기 --&gt;
    &lt;a href=&quot;#&quot;&gt;HOME&lt;/a&gt;
    &lt;p&gt;ID 수정 불가능, Password, Name 수정 가능&lt;/p&gt;

    &lt;!-- TODO: 각 input의 value 값을 로그인 한 사람의 정보로 채우기 --&gt;
    &lt;form name=&quot;form_profile&quot;&gt;
      &lt;label for=&quot;userid&quot;&gt;ID&lt;/label&gt;
      &lt;input type=&quot;hidden&quot; id=&quot;id&quot; value=&quot;&quot; /&gt;
      &lt;!-- readonly: userid는 수정 불가능 (조건임) --&gt;
      &lt;input
        id=&quot;userid&quot;
        type=&quot;text&quot;
        name=&quot;userid&quot;
        value=&quot;&lt;%= user.userid %&gt;&quot;
        readonly
      /&gt;
      &lt;label for=&quot;password&quot;&gt;Password&lt;/label&gt;
      &lt;input
        id=&quot;password&quot;
        type=&quot;password&quot;
        name=&quot;pw&quot;
        value=&quot;&lt;%= user.pw %&gt;&quot;
        required
      /&gt;
      &lt;label for=&quot;name&quot;&gt;Name&lt;/label&gt;
      &lt;input
        id=&quot;name&quot;
        type=&quot;text&quot;
        name=&quot;name&quot;
        value=&quot;&lt;%= user.name %&gt;&quot;
        required
      /&gt;
      &lt;button type=&quot;button&quot; onclick=&quot;profileEdit();&quot;&gt;Edit&lt;/button&gt;
      &lt;button type=&quot;button&quot; onclick=&quot;profileDelete();&quot;&gt;Delete&lt;/button&gt;
    &lt;/form&gt;

    &lt;script&gt;
      // TODO: [Login] 버튼 클릭시 서버에 회원 정보 수정 요청하기
      // POST /user/profile/edit
      function profileEdit() {}

      // TODO: [Login] 버튼 클릭시 서버에 회원 정보 삭제 요청하기
      // POST /user/profile/delete
      function profileDelete() {}
    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;
</code></pre><h4 id="signinejs"><strong><code>signin.ejs</code></strong></h4>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot;&gt;
  &lt;%- include(&#39;include/include&#39;, { title: &#39;로그인&#39; })%&gt;
  &lt;body&gt;
    &lt;h1&gt;로그인&lt;/h1&gt;
    &lt;script&gt;
      function login() {
        const data = {
          userid: document.getElementById(&quot;userid&quot;).value,
          pw: document.getElementById(&quot;password&quot;).value,
        };

        axios.post(&quot;/user/signin&quot;, data).then((response) =&gt; {
          if (response.data.success) {
            // 로그인 성공 시 사용자 정보를 form에 삽입 후 제출
            document.forms[&quot;form_info&quot;].userid.value =
              response.data.user.userid;
            document.forms[&quot;form_info&quot;].submit();
          } else {
            // 로그인 실패 시 알림 및 입력값 초기화
            alert(&quot;로그인 실패&quot;);
            document.forms[&quot;form_login&quot;].reset();
          }
        });
      }
    &lt;/script&gt;

    &lt;form name=&quot;form_info&quot; action=&quot;/user/profile&quot; method=&quot;POST&quot;&gt;
      &lt;input type=&quot;hidden&quot; name=&quot;userid&quot; /&gt;
    &lt;/form&gt;

    &lt;form name=&quot;form_login&quot;&gt;
      &lt;label for=&quot;userid&quot;&gt;ID&lt;/label&gt;
      &lt;input id=&quot;userid&quot; type=&quot;text&quot; name=&quot;userid&quot; required /&gt;&lt;br /&gt;
      &lt;label for=&quot;password&quot;&gt;Password&lt;/label&gt;
      &lt;input id=&quot;password&quot; type=&quot;password&quot; name=&quot;pw&quot; required /&gt;&lt;br /&gt;
      &lt;button type=&quot;button&quot; onclick=&quot;login();&quot;&gt;Login&lt;/button&gt;
    &lt;/form&gt;

    &lt;!-- 회원가입 페이지로 이동 --&gt;
    &lt;a href=&quot;/user/signup&quot;&gt;Register&lt;/a&gt;
  &lt;/body&gt;
&lt;/html&gt;
</code></pre><h4 id="signupejs"><strong><code>signup.ejs</code></strong></h4>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot;&gt;
  &lt;%- include(&#39;include/include&#39;, { title: &#39;회원가입&#39; })%&gt;
  &lt;body&gt;
    &lt;h1&gt;회원가입&lt;/h1&gt;
    &lt;script&gt;
      function register() {
        const data = {
          userid: document.getElementById(&quot;userid&quot;).value,
          pw: document.getElementById(&quot;password&quot;).value,
          name: document.getElementById(&quot;nickname&quot;).value,
        };

        axios.post(&quot;/user/signup&quot;, data).then((response) =&gt; {
          if (response.data.success) {
            // 회원가입 성공 시 로그인 페이지로 이동
            alert(&quot;회원가입 성공&quot;);
            document.location.href = &quot;/user/signin&quot;;
          } else {
            // 회원가입 실패 시 알림 표시
            alert(&quot;회원가입 실패&quot;);
          }
        });
      }
    &lt;/script&gt;

    &lt;form name=&quot;form_register&quot;&gt;
      &lt;label for=&quot;userid&quot;&gt;ID&lt;/label&gt;
      &lt;input id=&quot;userid&quot; type=&quot;text&quot; name=&quot;userid&quot; required /&gt;&lt;br /&gt;
      &lt;label for=&quot;password&quot;&gt;Password&lt;/label&gt;
      &lt;input id=&quot;password&quot; type=&quot;password&quot; name=&quot;pw&quot; required /&gt;&lt;br /&gt;
      &lt;label for=&quot;nickname&quot;&gt;Name&lt;/label&gt;
      &lt;input id=&quot;nickname&quot; type=&quot;text&quot; name=&quot;name&quot; required /&gt;&lt;br /&gt;
      &lt;button type=&quot;button&quot; onclick=&quot;register();&quot;&gt;Register&lt;/button&gt;
    &lt;/form&gt;

    &lt;!-- 로그인 페이지로 이동 --&gt;
    &lt;a href=&quot;/user/signin&quot;&gt;Login&lt;/a&gt;
  &lt;/body&gt;
&lt;/html&gt;
</code></pre><h4 id="appjs"><strong><code>app.js</code></strong></h4>
<pre><code>const express = require(&quot;express&quot;);
const app = express();
const PORT = 8000;

// 라우터 파일 불러오기
const userRouter = require(&quot;./routes/user&quot;);

// EJS 설정
app.set(&quot;view engine&quot;, &quot;ejs&quot;);
app.set(&quot;views&quot;, &quot;./views&quot;);

// 정적 파일 경로 설정
app.use(&quot;/static&quot;, express.static(__dirname + &quot;/static&quot;));

// body-parser 기능 (JSON 데이터 및 URL-encoded 데이터 파싱)
app.use(express.urlencoded({ extended: true }));
app.use(express.json());

// 기본 경로 처리
app.get(&quot;/&quot;, (req, res) =&gt; {
  res.redirect(&quot;/user/signin&quot;);
});

// 라우팅 설정
app.use(&quot;/user&quot;, userRouter);

// 404 에러 처리
app.use((req, res) =&gt; {
  res.status(404).render(&quot;404&quot;, { title: &quot;404 Error&quot; });
});

// 서버 실행
app.listen(PORT, () =&gt; {
  console.log(`http://localhost:${PORT}`);
});
</code></pre><pre><code>
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[실습 - MVC의 실습]]></title>
            <link>https://velog.io/@yoonsuk_choi/%EC%8B%A4%EC%8A%B5-MVC%EC%9D%98-%EC%8B%A4%EC%8A%B5</link>
            <guid>https://velog.io/@yoonsuk_choi/%EC%8B%A4%EC%8A%B5-MVC%EC%9D%98-%EC%8B%A4%EC%8A%B5</guid>
            <pubDate>Wed, 04 Dec 2024 03:20:15 GMT</pubDate>
            <description><![CDATA[<h2 id="mvc-패턴을-활용한-동적-폼-로그인-실습">MVC 패턴을 활용한 동적 폼 로그인 실습</h2>
<h3 id="핵심-내용">핵심 내용</h3>
<ul>
<li>동적폼 로그인 실습 문제를 MVC 패턴으로 변경해보기</li>
<li>기존 &#39;app.js&#39;에서 변수로 &#39;id&#39;와 &#39;pw&#39;를 선언했던 부분 &#39;model/User.js&#39;로 이동 후 &#39;exports&#39;</li>
<li>기존에 하나의 파일에서 관리되던 코드 구조를 역할별로 분리하여 유지보수성과 재사용성을 높이는 것이 목표이다.</li>
</ul>
<h3 id="mvc의-사용">MVC의 사용</h3>
<ul>
<li><p><strong>Model (User.js)</strong></p>
<ul>
<li>사용자 데이터를 관리하는 부분입니다. 사용자 정보(<code>realId</code>, <code>realPw</code>)를 담고 있으며, 이를 외부에서 접근할 수 있도록 <code>exports.userInfo()</code> 메서드로 제공하였습니다.</li>
</ul>
</li>
<li><p><strong>View (index.ejs)</strong></p>
<ul>
<li>사용자 인터페이스를 담당하는 부분입니다. 로그인 폼을 제공하며, 유효성 검증 및 Axios를 사용하여 데이터를 Controller로 전송합니다.</li>
</ul>
</li>
<li><p><strong>Controller (Cuser.js)</strong></p>
<ul>
<li>Model과 View를 연결하는 역할을 합니다. View에서 전달받은 요청 데이터를 Model과 비교하여 결과를 반환하는 로직을 포함하고 있습니다.</li>
</ul>
</li>
</ul>
<h3 id="간략한-폴더-구조">간략한 폴더 구조</h3>
<pre><code>project-MVC
├── controller/
│   ├── Cuser.js
├── model/
│   ├── User.js
├── routes/
│   ├── user.js
├── views/
│   ├── index.ejs
└── app.js</code></pre><hr>
<h3 id="appjs-실행-결과">app.js 실행 결과</h3>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/yoonsuk_choi/post/7b540104-bb90-4190-9d63-314ae89145f6/image.jpg" width="100%" height="100%"></th>
<th><img src="https://velog.velcdn.com/images/yoonsuk_choi/post/1f8b83b8-b299-413a-8630-50484f42b692/image.jpg" width="100%" height="100%"></th>
</tr>
</thead>
<tbody><tr>
<td>1. <strong>홈 화면</strong></td>
<td></td>
</tr>
<tr>
<td>- 경로에 접속하면 로그인 폼이 표시</td>
<td></td>
</tr>
</tbody></table>
<ol start="2">
<li><p><strong>로그인 기능</strong></p>
<ul>
<li>사용자가 아이디와 비밀번호를 입력한 후 &quot;로그인&quot; 버튼을 클릭하면, 입력된 데이터가 <code>/login</code> 경로로 POST 요청됩니다.</li>
<li>Controller(<code>Cuser.js</code>)는 Model(<code>User.js</code>)의 사용자 정보를 이용하여 입력값과 비교합니다.</li>
<li>로그인 성공 시 <code>{&quot;isSuccess&quot;: true, &quot;userId&quot;: &quot;apple&quot;}</code> 형식의 JSON 데이터를 반환하고, 실패 시 <code>{&quot;isSuccess&quot;: false}</code> 데이터를 반환합니다.</li>
</ul>
</li>
<li><p><strong>결과 표시</strong></p>
<ul>
<li>로그인 성공 or 실패 메시지가 표시됩니다.</li>
</ul>
</li>
</ol>
<h3 id="사용한-주요-기능">사용한 주요 기능</h3>
<ol>
<li><p><strong>Express를 이용한 서버 구성</strong></p>
<ul>
<li><code>app.js</code>에서 Express를 사용하여 미들웨어와 라우팅을 설정하고, 서버를 실행합니다.</li>
</ul>
</li>
<li><p><strong>EJS 템플릿 엔진</strong></p>
<ul>
<li><code>set(&quot;view engine&quot;, &quot;ejs&quot;)</code>를 통해 EJS를 템플릿 엔진으로 설정하였습니다.</li>
<li><code>index.ejs</code>를 이용하여 로그인 폼을 작성하였습니다.</li>
</ul>
</li>
<li><p><strong>Axios</strong></p>
<ul>
<li>View에서 데이터를 Controller로 전송하기 위해 Axios를 사용하였습니다.</li>
<li>비동기 요청을 통해 사용자 입력값을 <code>/login</code> 경로로 POST 요청합니다.</li>
</ul>
</li>
<li><p><strong>MVC 구조화</strong></p>
<ul>
<li>Model: <code>User.js</code>에서 사용자 정보를 관리.</li>
<li>View: <code>index.ejs</code>에서 UI 렌더링 및 사용자 인터랙션 관리.</li>
<li>Controller: <code>Cuser.js</code>에서 로직 처리 및 응답 제어.</li>
</ul>
</li>
<li><p><strong>데이터 검증 및 응답</strong></p>
<ul>
<li>Controller에서 사용자 입력값과 Model 데이터를 비교하여 결과를 반환.</li>
<li>성공 시 사용자 ID와 성공 메시지 반환, 실패 시 실패 메시지 반환.</li>
</ul>
</li>
</ol>
<h3 id="사용한-코드">사용한 코드</h3>
<h4 id="appjs">app.js</h4>
<pre><code>const express = require(&quot;express&quot;);
const app = express();
const PORT = 8080;

// 미들웨어 설정
app.set(&quot;view engine&quot;, &quot;ejs&quot;);
app.set(&quot;views&quot;, &quot;./views&quot;);

app.use(express.urlencoded({ extended: false }));
app.use(express.json());

// 라우터 연결
const userRouter = require(&quot;./routes/user&quot;);
app.use(&quot;/&quot;, userRouter);

// 포트 열기
app.listen(PORT, () =&gt; {
  console.log(`http://localhost:${PORT}`);
}); </code></pre><h4 id="indexejs">index.ejs</h4>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
    &lt;title&gt;MVC 실습&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h3&gt;동적폼 로그인 실습문제를 MVC 패턴으로 변경해보기&lt;/h3&gt;
    &lt;form name=&quot;login&quot;&gt;
      &lt;label for=&quot;userId&quot;&gt;
        &lt;span&gt;아이디&lt;/span&gt;
        &lt;input type=&quot;text&quot; name=&quot;userId&quot; id=&quot;userId&quot; required /&gt;
      &lt;/label&gt;
      &lt;br /&gt;
      &lt;label for=&quot;userPw&quot;&gt;
        &lt;span&gt;비밀번호&lt;/span&gt;
        &lt;input type=&quot;password&quot; name=&quot;userPw&quot; id=&quot;userPw&quot; required /&gt;
      &lt;/label&gt;

      &lt;br /&gt;
      &lt;button type=&quot;button&quot; onclick=&quot;clickLogin()&quot;&gt;로그인&lt;/button&gt;
    &lt;/form&gt;
    &lt;div class=&quot;login-result&quot;&gt;&lt;/div&gt;
  &lt;/body&gt;

  &lt;script src=&quot;https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js&quot;&gt;&lt;/script&gt;
  &lt;script&gt;
    function clickLogin() {
      const form = document.forms[&quot;login&quot;];
      const loginResult = document.querySelector(&quot;.login-result&quot;);
      const data = {
        userId: form.userId.value,
        userPw: form.userPw.value,
      };

      console.log(data);
      // 유효성 검증
      console.log(form.userId.checkValidity());
      console.log(form.userPw.checkValidity());
      if (!form.userId.checkValidity() || !form.userPw.checkValidity()) {
        loginResult.innerText = &quot;아이디와 비밀번호는 필수입니다.&quot;;
        return;
      }

      axios({
        url: &quot;/login&quot;,
        method: &quot;post&quot;,
        data: data,
      }).then((res) =&gt; {
        console.log(res.data);
        // {isSuccess: false}
        // {isSuccess: true, userId: &#39;banana&#39;}
        if (res.data.isSuccess) {
          loginResult.textContent = `${res.data.userId}님 로그인 성공~!😊`;
          loginResult.classList.add(&quot;success&quot;);
          loginResult.classList.remove(&quot;error&quot;);
        } else {
          loginResult.textContent = &quot;아이디 또는 패스워드 오류입니다🥺&quot;;
          loginResult.classList.add(&quot;error&quot;);
          loginResult.classList.remove(&quot;success&quot;);
        }
      });
    }
  &lt;/script&gt;
&lt;/html&gt;
</code></pre><h4 id="userjs">user.js</h4>
<pre><code>const express = require(&quot;express&quot;);
const router = express.Router();
const controller = require(&quot;../controller/Cuser&quot;);

// GET /
router.get(&quot;/&quot;, controller.main);

// POST /login
router.post(&quot;/login&quot;, controller.login);

module.exports = router;</code></pre><h4 id="userjs-1">User.js</h4>
<pre><code>exports.userInfo = () =&gt; {
  return {
    realId: &quot;apple&quot;,
    realPw: &quot;12345&quot;,
  };
};</code></pre><h4 id="cuserjs">Cuser.js</h4>
<pre><code>const User = require(&quot;../model/User&quot;);

exports.main = (req, res) =&gt; {
  res.render(&quot;index&quot;);
};

exports.login = (req, res) =&gt; {
  console.log(req.body);
  const { realId, realPw } = User.userInfo();

  if (realId === req.body.userId &amp;&amp; realPw === req.body.userPw) {
    res.send({ isSuccess: true, userId: req.body.userId });
  } else {
    res.send({ isSuccess: false });
  }
};</code></pre><hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[학습 - MVC의 사용]]></title>
            <link>https://velog.io/@yoonsuk_choi/%ED%95%99%EC%8A%B5-MVC%EC%9D%98-%EC%82%AC%EC%9A%A9</link>
            <guid>https://velog.io/@yoonsuk_choi/%ED%95%99%EC%8A%B5-MVC%EC%9D%98-%EC%82%AC%EC%9A%A9</guid>
            <pubDate>Wed, 04 Dec 2024 02:38:59 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/yoonsuk_choi/post/a66e322e-74af-4bf6-92f2-3bf942afcba1/image.JPG" alt=""></p>
<p><strong>학습 - Windows 환경에서 MVC 구조 이해 및 학습</strong></p>
<hr>
<h3 id="mvc란-무엇인가">MVC란 무엇인가?</h3>
<p><img src="https://velog.velcdn.com/images/yoonsuk_choi/post/44389832-930b-4531-b8bd-629412796444/image.png" alt=""></p>
<h4 id="정의">정의</h4>
<ul>
<li><strong>MVC (Model-View-Controller):</strong><br>소프트웨어 설계 패턴 중 하나로, 애플리케이션의 구조를 분리하여 유지보수성과 확장성을 높이는 데 초점<ul>
<li><strong>Model:</strong> 데이터 및 비즈니스 로직을 담당  </li>
<li><strong>View:</strong> 사용자에게 보여지는 화면 담당  </li>
<li><strong>Controller:</strong> 사용자 입력을 처리하고 Model과 View 간의 연결 담당  </li>
</ul>
</li>
</ul>
<h4 id="mvc의-이점">MVC의 이점</h4>
<ol>
<li><strong>역할 분리:</strong><br>각각의 구성 요소가 독립적으로 동작하도록 설계  </li>
<li><strong>유지보수 용이:</strong><br>특정 기능 변경 시 관련된 부분만 수정 가능  </li>
<li><strong>재사용성:</strong><br>View와 Model이 분리되어 다양한 View에서 같은 데이터를 활용 가능  </li>
</ol>
<hr>
<h3 id="windows-환경에서-mvc-구현">Windows 환경에서 MVC 구현</h3>
<h4 id="준비물">준비물</h4>
<ol>
<li><p><strong>Node.js 설치</strong>   </p>
</li>
<li><p><strong>확장자 설치:</strong>
```
[Nod.js]</p>
</li>
<li><p>터미널 실행</p>
</li>
</ol>
<p>1.터미널 위치 조정
cd [작업할 폴더 경로]
Ex) cd C:\Users\Administrator\Desktop\SeSAC\00_MVC 학습</p>
<p>2.패키지 초기화 및 설치
[Node.js]</p>
<ol>
<li><p>기본값 : npm init -y
=&quot;package.json&quot;게 생성된다.</p>
</li>
<li><p>이름지정 : npm install [패키지 이름]</p>
</li>
<li><p>package-lock.json 생성
npm install ejs</p>
</li>
</ol>
<p>4.Express/의존성 설치(웹 구현 준비)
npm install express --save
이거 있어야함.
&#39;&#39;&#39;
express
&#39;&#39;&#39;</p>
<p>5.[.gitignore] 파일 작성(package-lock.json이 너무 커서 따로 분류해야함)
꼭 안에 
&#39;&#39;&#39;
/node_modules
package-lock.json
&#39;&#39;&#39;
를 적어 넣어야 사용 할 수 있다.</p>
<pre><code>3. **실행**  
   - node 파일 이름.js
   - Ex) node missing.js


#### 프로젝트 초기화 및 구조 생성
1. **MVC 폴더 구조 생성**
- node에서 설치 하는 방법도 있지만 익숙하지 않을 때는 직접 설치 해도 좋다.
- 폴더는 따로 생성 해주면 편하다.
- 내부 [ .js ] 또한 경로에 맞게 만들어주면 편함.
   ```plaintext
   my-mvc-app/
   ├── controller/
   │   └── Cuser.js
   ├── model/
   │   └── User.js
   ├── routes/
   │   └── user.js
   ├── views/
   │   └── index.ejs
   ├── app.js
   └── package.json</code></pre><hr>
<h4 id="mvc-주요-파일-작성">MVC 주요 파일 작성</h4>
<ol>
<li><p><strong><code>app.js</code> (앱 초기화 및 기본 설정)</strong>  </p>
<pre><code class="language-javascript">const express = require(&#39;express&#39;);
const app = express();
const userRouter = require(&#39;./routes/user&#39;);

app.set(&#39;view engine&#39;, &#39;ejs&#39;);
app.set(&#39;views&#39;, &#39;./views&#39;);

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.use(&#39;/user&#39;, userRouter);

app.listen(3000, () =&gt; {
    console.log(&#39;Server is running on http://localhost:3000&#39;);
});</code></pre>
</li>
<li><p><strong><code>routes/user.js</code> (라우팅 설정)</strong>  </p>
<pre><code class="language-javascript">const express = require(&#39;express&#39;);
const router = express.Router();
const userController = require(&#39;../controller/Cuser&#39;);

router.get(&#39;/&#39;, userController.getUsers);
router.post(&#39;/&#39;, userController.createUser);

module.exports = router;</code></pre>
</li>
<li><p><strong><code>controller/Cuser.js</code> (컨트롤러 정의)</strong>  </p>
<pre><code class="language-javascript">const User = require(&#39;../model/User&#39;);

exports.getUsers = (req, res) =&gt; {
    const users = User.getAll();
    res.render(&#39;index&#39;, { users });
};

exports.createUser = (req, res) =&gt; {
    const newUser = req.body;
    User.add(newUser);
    res.redirect(&#39;/user&#39;);
};</code></pre>
</li>
<li><p><strong><code>model/User.js</code> (데이터 관리)</strong>  </p>
<pre><code class="language-javascript">let users = [];

exports.getAll = () =&gt; users;

exports.add = (user) =&gt; {
    users.push(user);
};</code></pre>
</li>
<li><p><strong><code>views/index.ejs</code> (화면 구성)</strong>  </p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;User List&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;User List&lt;/h1&gt;
    &lt;ul&gt;
        &lt;% users.forEach(user =&gt; { %&gt;
            &lt;li&gt;&lt;%= user.name %&gt;&lt;/li&gt;
        &lt;% }); %&gt;
    &lt;/ul&gt;
    &lt;form action=&quot;/user&quot; method=&quot;POST&quot;&gt;
        &lt;input type=&quot;text&quot; name=&quot;name&quot; placeholder=&quot;Enter user name&quot;&gt;
        &lt;button type=&quot;submit&quot;&gt;Add User&lt;/button&gt;
    &lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
</li>
</ol>
<hr>
<h3 id="학습-포인트">학습 포인트</h3>
<ol>
<li><strong>Express.js를 사용한 서버 생성:</strong>  <ul>
<li>기본적인 라우팅 및 미들웨어 사용  </li>
</ul>
</li>
<li><strong>EJS를 활용한 동적 View 구성:</strong>  <ul>
<li>데이터를 View에 전달하여 동적으로 렌더링  </li>
</ul>
</li>
<li><strong>Model-Controller 간의 데이터 전달:</strong>  <ul>
<li>Model에서 데이터를 가져오고 Controller에서 View로 전달  </li>
</ul>
</li>
<li><strong>폼 데이터를 처리하고 사용자 추가 기능 구현:</strong>  <ul>
<li><code>express.urlencoded</code>를 사용하여 POST 요청 처리  </li>
</ul>
</li>
</ol>
<pre><code>코드를 입력하세요</code></pre>]]></description>
        </item>
    </channel>
</rss>