Node.js 학습형 3주차

송채원·2025년 5월 4일

Node.js

목록 보기
3/3

< 실전 프로젝트- 나만의 블로그 앱 만들기>

🧑‍💻 1. 사용자 역할 및 기능 정의

블로그 사용자 → 일반 사용자 / 관리자

사용자 종류기능
일반 사용자게시물 목록 조회 / 게시물 보기 / 메뉴 이동 / 로그인
관리자새 글 작성 / 수정 / 삭제 / 로그아웃

2. 개발 환경 세팅

  1. 새 폴더 생성 후 터미널에서 npm 초기화

    npm init -y
  2. 주요 모듈 설치

    npm install express dotenv
  3. app.js 생성 및 기본 서버 코드 작성
    (포트는 .env에 지정하거나 기본값 3000 사용)

3. 라우트 기본 설정

  1. routes 폴더 생성 → main.js 생성
  2. app.js에서 해당 라우터 연결
  3. ‘Hello World’ 테스트 후 라우트로 분리하여 구조화

4. 프로젝트 폴더 구조

아래 폴더들을 만들어 코드 분리

폴더역할
configDB 연결 설정
modelsDB 스키마 정의
publicCSS, JS, 이미지 등 정적 파일
routes라우팅 파일들
viewsEJS 템플릿 저장 (브라우저에 표시될 화면)

물론입니다! 아래는 "5. EJS 템플릿 엔진 및 레이아웃 구현 B" 파트와 "6. 동적 콘텐츠 표시를 위한 변수 전달"까지를 누락 없이 간결하고 명확하게 정리한 요약입니다:


5. EJS 템플릿 엔진 및 레이아웃 구현

📌 중복되는 HTML을 줄이기 위해 EJS 템플릿과 레이아웃 방식 사용

✅ EJS 레이아웃 개념

  • 모든 화면에 공통되는 틀(main layout)을 만들고, 화면별로 다른 콘텐츠만 끼워 넣는 구조
  • 레이아웃 안에 <%- body %>를 통해 개별 페이지 내용을 렌더링

설치 및 설정

  1. 필요한 모듈 설치
npm install ejs express-ejs-layouts
  1. app.js에 설정 추가
const express = require('express');
const expressLayouts = require('express-ejs-layouts');
const app = express();

app.set('view engine', 'ejs');
app.use(expressLayouts);
app.set('layout', 'layouts/main');

📁 레이아웃 및 뷰 파일 구성

  • views/
    • layouts/main.ejs → 전체 공통 레이아웃 (<%- body %> 포함)
    • index.ejs → 홈 콘텐츠 (예:

      )
    • about.ejs → 어바웃 콘텐츠 (

      어바웃

      )

📌 routes/main.js 설정 예시

const express = require('express');
const router = express.Router();

router.get('/', (req, res) => {
  res.render('index');
});

router.get('/about', (req, res) => {
  res.render('about');
});

module.exports = router;
  • layout 경로는 app.js에서 전역으로 설정했기 때문에 render할 때 따로 지정할 필요 없음

🧪 동작 확인

  • / 경로 요청 시 → main.ejs 레이아웃 + index.ejs 콘텐츠 출력
  • /about 경로 요청 시 → main.ejs 레이아웃 + about.ejs 콘텐츠 출력
  • <%- body %>에 각 페이지의 내용이 삽입된 것을 HTML 소스 보기로 확인 가능

6. 동적 콘텐츠 표시를 위한 변수 전달

페이지마다 다른 제목을 보여주기 위해 변수 사용

✏️ 레이아웃(main.ejs)에 제목 영역 추가

views/layouts/main.ejs

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <title><%= title %></title>
</head>
<body>
  <%- body %>
</body>
</html>

👉 이때 title 변수는 각 페이지에서 render시 넘겨줘야 함

🛠 routes/main.js에서 title 전달하기

router.get('/', (req, res) => {
  res.render('index', { title: '홈' });
});

router.get('/about', (req, res) => {
  res.render('about', { title: '어바웃' });
});

✅ 결과

  • 브라우저 탭의 제목이 "홈" 혹은 "어바웃"으로 동적으로 변경됨
  • layout 파일에서 전달한 title 값을 받아서 동작

물론입니다! 아래는 요청하신 블로그 앱의 관리자 페이지 및 로그인 기능 구현 내용을 Velog 형식의 학습내용 정리글로 구성한 예시입니다. 내용 누락 없이 간결하고 이해하기 쉽도록 구성했습니다.


Node.js 블로그 앱 - 관리자 페이지 & 로그인 기능 구현 (JWT)


1. 사용자 vs 관리자 레이아웃

📌 블로그에는 2가지 화면이 존재합니다.

구분기능
사용자게시물 조회, 로그인 링크 표시
관리자게시물 수정/삭제, 새 글 작성, 로그아웃 링크 표시
  • 사용자 레이아웃(main.ejs) → 기본 블로그 화면
  • 관리자 레이아웃(admin.ejs) → 로그아웃 메뉴 포함 / 제목 변경
  • 로그인 전용 레이아웃(admin_no_logout.ejs) → 로그아웃 메뉴 제거

views/layouts/ 폴더에 각각 저장


2. 관리자 로그인 화면 & 라우트 구성

  • routes/admin.js 파일 생성 → /admin 요청 처리
  • 로그인 화면(index.ejs)은 views/admin/ 폴더에 위치
  • 로그인 폼에는 username, password 입력 필드

📌 /admin GET 요청 시 → 로그인 화면 렌더링
📌 app.js에 admin 라우터 연결


3. 로그인 상태에 따른 관리자 레이아웃 분리

관리자 로그인 전에는 로그아웃 링크가 보이면 안되므로,

  • admin_no_logout.ejs : 로그인 전 레이아웃
  • admin.ejs : 로그인 후 레이아웃

라우트에서 조건에 따라 해당 레이아웃 사용 지정


4. 관리자 정보 등록 (DB 저장)

  • 관리자 등록용 임시 폼 작성 (index.ejs 내부)
  • /register 경로에서 GET → 등록 폼, POST → DB 저장

▶ models/user.js
MongoDB Mongoose 스키마 정의

const UserSchema = new mongoose.Schema({
  username: String,
  password: String
})

5. 비밀번호 암호화 처리

  • bcrypt 모듈 설치
  • POST 요청 받을 시 비밀번호 해싱 후 저장

📦 필요한 모듈

npm install bcrypt

등록 처리 시:

  • 해싱 → bcrypt.hash()
  • DB 저장 → User.create({ username, password: hashedPassword })

요청 본문 파싱을 위한 미들웨어 필요

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

6. 관리자 로그인 (JWT 기반)

  • 로그인 성공 시 JWT 토큰 발행
  • 토큰은 클라이언트 쿠키에 저장

📦 설치 모듈

npm install jsonwebtoken cookie-parser

app.js 설정:

const cookieParser = require('cookie-parser')
app.use(cookieParser())

.env에 비밀키 추가:

JWT_SECRET=mysecretkey123

로그인 라우트 (/admin POST):

  • 사용자 존재 확인
  • 비밀번호 bcrypt.compare()
  • JWT.sign() → 토큰 생성
  • res.cookie("token", token)
  • 로그인 성공 시 /allposts 로 리다이렉트

7. 관리자 게시물 목록 화면

  • /allposts 경로에서 Post 모델로 전체 게시물 조회
  • views/admin/allposts.ejs 렌더링

화면 구성:

  • 게시물 제목 리스트 출력
  • 게시물 보기 링크
  • 편집(GET), 삭제(DELETE) 버튼
  • 새 게시물 작성 링크

레이아웃: admin.ejs (로그아웃 포함)


8. 관리자 로그아웃 기능

  • 로그아웃 링크는 /logout 경로
  • 로그아웃 처리: 쿠키의 token 삭제

admin.js에 라우트 작성:

router.get('/logout', (req, res) => {
  res.clearCookie('token')
  res.redirect('/')
})

admin.ejs에서 링크 수정:

<a href="/logout">로그아웃</a>

9. 사용자 화면의 로그인 링크 연결

  • main.ejs 에 있는 관리자 로그인 링크 → /admin 으로 수정
<a href="/admin">관리자 로그인</a>

요청 시 로그인 화면(index.ejs) 렌더링되도록 구성


📌 핵심 구현 흐름 요약

기능설명모듈/기술
사용자 등록username / password 저장mongoose
비밀번호 암호화bcrypt hash() → DB 저장bcrypt
로그인사용자 확인 + 비밀번호 비교 후 JWT 발급jsonwebtoken
인증 유지JWT 토큰을 쿠키에 저장cookie-parser
토큰 검증관리자 페이지 접근 시 토큰 유효성 확인middleware 작성 가능
게시물 출력로그인 후 전체 게시물 조회 가능MongoDB, ejs
로그아웃 처리쿠키 삭제 후 첫 화면으로 이동res.clearCookie()

물론입니다! 주어진 CRUD 기능 구현 관련 내용을 빠짐없이 Velog 스타일로 학습 정리로 구성해드릴게요.


Node.js 블로그 앱 - CRUD 기능 구현과 관리자 인증 절차


핵심 용어 정리

용어설명
Node.js자바스크립트를 서버에서도 실행할 수 있도록 해주는 런타임 환경
CRUDCreate, Read, Update, Delete의 약어로 데이터 처리 기본 기능
API요청과 응답을 처리하는 인터페이스 (라우트)
Middleware요청(req)과 응답(res) 사이에서 작업을 수행하는 함수
JWTJSON Web Token, 인증 토큰 방식
Method-OverrideHTML form에서 PUT, DELETE 요청을 가능하게 함

1. CRUD API 경로 및 요청 방식 설정

Backend의 핵심인 CRUD API를 아래와 같이 구성했습니다:

요청 경로메서드설명
/addGET게시물 작성 폼 출력
/addPOST게시물 DB에 저장
/edit/:idGET특정 게시물 수정 폼 출력
/edit/:idPUT게시물 내용 업데이트
/delete/:idDELETE게시물 삭제 요청 처리
  • 게시물 생성, 수정, 삭제는 오직 관리자만 접근 가능하도록 설정
  • 각 요청은 라우트 코드와 EJS 뷰 파일로 연결됨

2. 새 게시물 작성 기능 구현

  • 관리자 전용 화면에서 “글 작성” 버튼 클릭 시 → /add 경로로 이동
  • add.ejs: 글 제목, 내용 입력 폼 구현
  • admin.js 라우터에 GET/POST 라우터 작성
  • POST 요청 시 DB에 저장 후 /allposts로 리다이렉트

📌 admin.js 예:

router.get('/add', checkLogin, (req, res) => {
  res.render('admin/add', { layout: 'layouts/admin' });
});

router.post('/add', checkLogin, async (req, res) => {
  await Post.create(req.body);
  res.redirect('/allposts');
});

3. 관리자 로그인 확인 미들웨어

모든 CRUD 작업은 로그인한 관리자만 가능하도록 보안 처리

✅ checkLogin 미들웨어 제작:

  • 쿠키(token)를 확인
  • JWT 비밀키 확인 후 토큰 유효성 검증
  • 실패 시 /admin(로그인 창)으로 리다이렉트

🔒 인증이 필요한 라우트마다 checkLogin 미들웨어 적용

router.get('/add', checkLogin, ...);
router.get('/edit/:id', checkLogin, ...);

4. 편집 기능 구현 준비

  • 게시물 편집 버튼 클릭 시 /edit/:id GET 요청
  • edit.ejs: 기존 글 제목, 내용 pre-filled form
  • 수정 후 ‘수정 완료’ 클릭 시 PUT 요청으로 서버 전송

📦 method-override 사용

npm install method-override

app.js 설정:

const methodOverride = require('method-override');
app.use(methodOverride('_method'));

edit.ejs 폼 내에 method override 적용 예:

<form action="/edit/<%= post._id %>?_method=PUT" method="POST">

5. 게시물 수정 처리 라우터 구현

/admin.js 라우트:

// 수정 폼 출력
router.get('/edit/:id', checkLogin, async (req, res) => {
  const post = await Post.findById(req.params.id);
  res.render('admin/edit', { post, layout: 'layouts/admin' });
});

// 수정 반영 요청
router.put('/edit/:id', checkLogin, async (req, res) => {
  await Post.findByIdAndUpdate(req.params.id, req.body);
  res.redirect('/allposts');
});

6. 게시물 삭제 기능 구현

  • 삭제는 게시물 목록 / 편집 화면 양쪽에서 가능해야 함
  • form을 통해 DELETE 요청 처리 (method-override 사용)
  • 삭제 처리는 /delete/:id 경로에서 수행

admin.js 코드:

router.delete('/delete/:id', checkLogin, async (req, res) => {
  await Post.findByIdAndDelete(req.params.id);
  res.redirect('/allposts');
});

삭제 버튼 HTML 예:

<form action="/delete/<%= post._id %>?_method=DELETE" method="POST">
  <button type="submit" onclick="return confirm('삭제하시겠습니까?')">삭제</button>
</form>

7. CRUD 기능 구현 테스트

  • /add로 새 글 작성 후 → DB에 정상 저장 확인
  • /edit/:id 에서 기존 글 수정 → 정상 반영됨
  • 삭제 버튼 클릭 시 → 데이터베이스에서 데이터 삭제됨
  • 모든 과정에서 로그인 여부 체크 완료

✨학습한 점

JWT로 로그인 기능을 만들면서, 단순히 로그인만 되는 게 중요한 게 아니라, 로그인한 사용자가 진짜 맞는지 확인하고, 로그인하지 않은 사람은 못 들어오게 막는 것이 더 중요하다는 걸 알게 됐습니다. 이런 보안 처리를 미들웨어로 하는 방법도 배울 수 있었습니다.
또한, HTML 폼에서는 기본적으로 GET과 POST 방식만 지원해서 글을 수정하거나 삭제할 때는 좀 불편했는데, method-override를 이용해 PUT이나 DELETE 요청처럼 처리하는 방법도 알게 돼서 많이 도움이 됐습니다.

0개의 댓글