⚙️ 백엔드 개발자 되기 — A-Z 완전 정복 가이드
비전공자도 OK! 음식점 비유로 쉽게 배우는 서버 개발 | 예상 학습 기간: 3~5개월
백엔드(Backend)란? 눈에 보이지 않는 서버 쪽을 만드는 것입니다.
음식점 비유: 손님(프론트엔드)은 메뉴를 보고 주문하고, 주방(백엔드)에서 실제로 음식을 만들고, 재료는 냉장고(데이터베이스)에 보관합니다!
프론트가 "화면"을 만든다면, 백엔드는 "데이터와 로직"을 처리합니다.
🌐 1단계 — 서버와 인터넷의 동작 원리 이해 (1주)
서버란? 24시간 365일 켜져 있으면서 요청을 기다리는 컴퓨터입니다.
여러분이 유튜브를 클릭하면 → 구글 서버가 그 요청을 받아 → 영상 데이터를 보내줍니다.
이 과정이 모두 HTTP 프로토콜을 통해 이루어집니다.
// HTTP 요청/응답 기본 구조
// 클라이언트 → 서버
GET /api/users HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJ...token
// 서버 → 클라이언트
HTTP/1.1 200 OK
Content-Type: application/json
{
"users": [
{ "id": 1, "name": "홍길동", "email": "hong@example.com" }
]
}
🟨 2단계 — Node.js 기초 (3~4주)
Node.js란? JavaScript를 브라우저가 아닌 서버에서 실행할 수 있게 해주는 런타임입니다.
프론트엔드에서 JavaScript를 배웠다면, 같은 언어로 백엔드까지 만들 수 있어요!
# Node.js 설치 확인
node --version # v20.x.x
npm --version # 10.x.x
# 프로젝트 초기화
mkdir my-server && cd my-server
npm init -y
// server.js — Node.js 내장 http 모듈로 서버 만들기
const http = require('http');
const server = http.createServer((req, res) => {
console.log('요청 URL:', req.url);
console.log('요청 메서드:', req.method);
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
res.end(JSON.stringify({ message: '안녕하세요! 첫 번째 서버입니다!' }));
});
server.listen(3000, () => {
console.log('서버가 3000번 포트에서 실행 중입니다 🚀');
console.log('브라우저에서 http://localhost:3000 접속해보세요!');
});
📌 Node.js 핵심 개념
• CommonJS (require) vs ESM (import) — 모듈 시스템 두 가지
• 비동기(Async) — Node.js는 싱글 스레드지만 비동기로 수천 명 동시 처리 가능
• 이벤트 루프 — 요청이 오면 큐에 넣고 순서대로 처리
• npm — 전 세계 200만+ 패키지 설치 관리자
// 파일 시스템 (fs) 모듈 — 파일 읽기/쓰기
const fs = require('fs');
// 비동기 방식 (권장)
fs.readFile('./data.json', 'utf-8', (err, data) => {
if (err) throw err;
console.log(JSON.parse(data));
});
// Promise 방식 (더 현대적)
const fsPromises = require('fs/promises');
async function readData() {
const data = await fsPromises.readFile('./data.json', 'utf-8');
return JSON.parse(data);
}
// path 모듈 — 경로 다루기
const path = require('path');
const filePath = path.join(__dirname, 'data', 'users.json');
⚡ 3단계 — Express.js로 웹 서버 만들기 (2~3주)
Express란? Node.js로 웹 서버를 쉽게 만들 수 있는 미니멀 프레임워크입니다.
순수 Node.js보다 훨씬 간결하게 라우팅, 미들웨어, 에러 처리를 할 수 있습니다.
npm install express cors dotenv
npm install -D nodemon # 파일 변경 시 자동 재시작
// app.js — Express 기본 구조
const express = require('express');
const cors = require('cors');
require('dotenv').config();
const app = express();
// ===== 미들웨어 설정 =====
app.use(cors()); // CORS 허용 (프론트엔드와 통신)
app.use(express.json()); // JSON 요청 body 파싱
app.use(express.urlencoded({ extended: true }));
// ===== 라우터 연결 =====
app.use('/api/users', require('./routes/users'));
app.use('/api/posts', require('./routes/posts'));
// ===== 에러 핸들러 =====
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: '서버 내부 오류가 발생했습니다.' });
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log('서버 실행 중 :' + PORT));
module.exports = app;
// routes/users.js — 사용자 라우터
const express = require('express');
const router = express.Router();
// 임시 데이터 (DB 연결 전 테스트용)
let users = [
{ id: 1, name: '홍길동', email: 'hong@example.com' },
{ id: 2, name: '김영희', email: 'kim@example.com' },
];
// GET /api/users — 목록 조회
router.get('/', (req, res) => {
const { page = 1, limit = 10, search } = req.query;
let result = users;
if (search) result = result.filter(u => u.name.includes(search));
res.json({ data: result, total: result.length });
});
// GET /api/users/:id — 단건 조회
router.get('/:id', (req, res) => {
const user = users.find(u => u.id === Number(req.params.id));
if (!user) return res.status(404).json({ error: '사용자를 찾을 수 없습니다.' });
res.json(user);
});
// POST /api/users — 생성
router.post('/', (req, res) => {
const { name, email } = req.body;
if (!name || !email) {
return res.status(400).json({ error: '이름과 이메일은 필수입니다.' });
}
const newUser = { id: Date.now(), name, email };
users.push(newUser);
res.status(201).json(newUser);
});
// PUT /api/users/:id — 수정
router.put('/:id', (req, res) => {
const idx = users.findIndex(u => u.id === Number(req.params.id));
if (idx === -1) return res.status(404).json({ error: '없음' });
users[idx] = { ...users[idx], ...req.body };
res.json(users[idx]);
});
// DELETE /api/users/:id — 삭제
router.delete('/:id', (req, res) => {
users = users.filter(u => u.id !== Number(req.params.id));
res.status(204).send();
});
module.exports = router;
🔧 미들웨어 (Middleware) 완전 이해
미들웨어는 요청(Request)이 라우터에 도달하기 전에 거치는 중간 처리 함수입니다.
마치 음식점의 직원들처럼: 손님 맞이 → 주문 받기 → 주방 전달 → 음식 서빙 순서로 처리됩니다.
// 미들웨어 종류와 사용 패턴
const express = require('express');
const app = express();
// 1. 로깅 미들웨어 (모든 요청에 적용)
app.use((req, res, next) => {
console.log(new Date().toISOString() + ' ' + req.method + ' ' + req.url);
next(); // 반드시 next() 호출 → 다음 미들웨어로 이동
});
// 2. 인증 미들웨어 (특정 라우터에만)
const authMiddleware = (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) return res.status(401).json({ error: '토큰이 없습니다.' });
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch {
res.status(401).json({ error: '유효하지 않은 토큰입니다.' });
}
};
// 보호된 라우트에만 적용
app.get('/api/profile', authMiddleware, (req, res) => {
res.json({ user: req.user });
});
// 3. 입력 검증 미들웨어 (express-validator 활용)
// npm install express-validator
🌐 4단계 HTTP 메서드 & REST API 설계
🗄️ 5단계 — 데이터베이스 연결 (2~3주)
데이터베이스란? 데이터를 체계적으로 저장하고 빠르게 찾을 수 있는 전자 창고입니다.
엑셀 표(시트)처럼 행(row)과 열(column)로 데이터를 저장하는 것이 관계형 DB(RDBMS)입니다.
# MySQL 설치 및 연결
npm install mysql2 prisma @prisma/client
# Prisma 초기화 (ORM 설정)
npx prisma init
// prisma/schema.prisma — 데이터 모델 정의
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
name String
email String @unique
password String
role Role @default(USER)
posts Post[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Post {
id Int @id @default(autoincrement())
title String
content String @db.Text
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
createdAt DateTime @default(now())
}
enum Role {
USER
ADMIN
}
// db/prisma.js — Prisma 클라이언트 싱글톤
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
module.exports = prisma;
// routes/posts.js — Prisma로 DB 조작
const prisma = require('../db/prisma');
// 게시글 목록 (페이지네이션)
router.get('/', async (req, res) => {
const { page = 1, limit = 10 } = req.query;
const skip = (page - 1) * limit;
const [posts, total] = await Promise.all([
prisma.post.findMany({
skip: Number(skip),
take: Number(limit),
orderBy: { createdAt: 'desc' },
include: { author: { select: { name: true } } }, // 작성자 이름도 함께
}),
prisma.post.count(),
]);
res.json({ data: posts, total, page: Number(page), totalPages: Math.ceil(total / limit) });
});
// 게시글 생성
router.post('/', authMiddleware, async (req, res) => {
const { title, content } = req.body;
const post = await prisma.post.create({
data: { title, content, authorId: req.user.userId },
});
res.status(201).json(post);
});
// 게시글 수정 (본인만)
router.put('/:id', authMiddleware, async (req, res) => {
const post = await prisma.post.findUnique({ where: { id: Number(req.params.id) } });
if (!post) return res.status(404).json({ error: '없음' });
if (post.authorId !== req.user.userId) return res.status(403).json({ error: '권한 없음' });
const updated = await prisma.post.update({
where: { id: Number(req.params.id) },
data: req.body,
});
res.json(updated);
});
🔐 6단계 — 인증과 보안 (2~3주)
npm install jsonwebtoken bcryptjs
// routes/auth.js — 회원가입 & 로그인
const express = require('express');
const router = express.Router();
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const prisma = require('../db/prisma');
// 회원가입
router.post('/register', async (req, res) => {
const { name, email, password } = req.body;
// 이메일 중복 확인
const existing = await prisma.user.findUnique({ where: { email } });
if (existing) return res.status(400).json({ error: '이미 사용 중인 이메일입니다.' });
// 비밀번호 해시화 (절대 평문으로 저장 금지!)
const hashedPw = await bcrypt.hash(password, 12); // saltRounds: 12
const user = await prisma.user.create({
data: { name, email, password: hashedPw },
select: { id: true, name: true, email: true }, // 비밀번호 제외
});
res.status(201).json({ message: '회원가입 성공', user });
});
// 로그인
router.post('/login', async (req, res) => {
const { email, password } = req.body;
const user = await prisma.user.findUnique({ where: { email } });
if (!user) return res.status(401).json({ error: '이메일 또는 비밀번호가 올바르지 않습니다.' });
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) return res.status(401).json({ error: '이메일 또는 비밀번호가 올바르지 않습니다.' });
// Access Token (짧은 수명) + Refresh Token (긴 수명) 패턴
const accessToken = jwt.sign(
{ userId: user.id, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: '1h' }
);
const refreshToken = jwt.sign(
{ userId: user.id },
process.env.JWT_REFRESH_SECRET,
{ expiresIn: '7d' }
);
res.json({ accessToken, refreshToken });
});
module.exports = router;
🔒 보안 필수 체크리스트
• 비밀번호는 bcrypt로 해시화 (절대 평문 저장 금지)
• API 키, 비밀번호는 .env 파일에만 저장 (GitHub에 절대 올리지 않기)
• SQL Injection 방지 — ORM(Prisma) 사용 또는 준비된 쿼리(Prepared Statement)
• HTTPS 사용 — 배포 시 자동 적용 (Railway, Render 등)
• Rate Limiting — 같은 IP에서 과도한 요청 제한 (express-rate-limit)
• CORS 설정 — 허용된 도메인만 접근 가능하도록
// 보안 미들웨어 추가
npm install helmet express-rate-limit
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
app.use(helmet()); // 보안 HTTP 헤더 설정
// 로그인 API에 Rate Limiting (1분에 5번만)
const loginLimiter = rateLimit({
windowMs: 60 * 1000, // 1분
max: 5,
message: { error: '너무 많은 시도입니다. 잠시 후 다시 시도해주세요.' },
});
app.use('/api/auth/login', loginLimiter);
🚀 7단계 — 서버 배포 완전 가이드
# Railway 배포 (가장 쉬운 방법)
# 1. railway.app 접속 → GitHub 로그인
# 2. "New Project" → GitHub 저장소 선택
# 3. 환경변수 설정 (Variables 탭에서 .env 내용 입력)
# 4. 자동 배포 완료! 도메인 생성
# package.json에 start 스크립트 추가 (필수!)
{
"scripts": {
"start": "node app.js",
"dev": "nodemon app.js",
"db:migrate": "prisma migrate deploy"
}
}
# .env 파일 (로컬용 — .gitignore에 추가!)
DATABASE_URL="mysql://user:password@localhost:3306/mydb"
JWT_SECRET="your-super-secret-key-at-least-32-chars"
JWT_REFRESH_SECRET="another-secret-key"
PORT=3000
NODE_ENV=development
# Railway 대시보드에도 같은 내용 입력
🎯 8단계 — 실전 프로젝트: 게시판 API 완성
지금까지 배운 모든 것을 합쳐 완전한 게시판 REST API를 만들어봅니다.
회원가입 → 로그인 → 글쓰기 → 조회 → 수정 → 삭제 전체 사이클을 구현합니다.
// 프로젝트 구조 (현업 표준)
my-backend/
├── src/
│ ├── routes/
│ │ ├── auth.js # 회원가입, 로그인
│ │ ├── users.js # 사용자 CRUD
│ │ └── posts.js # 게시글 CRUD
│ ├── middleware/
│ │ ├── auth.js # JWT 인증 미들웨어
│ │ ├── validate.js # 입력 검증
│ │ └── upload.js # 파일 업로드 (multer)
│ ├── db/
│ │ └── prisma.js # DB 클라이언트
│ └── utils/
│ └── ApiError.js # 커스텀 에러 클래스
├── prisma/
│ └── schema.prisma
├── .env
├── .gitignore
├── app.js
└── package.json
// Postman으로 API 테스트하기
// 1. 회원가입
POST http://localhost:3000/api/auth/register
Body (JSON): { "name": "홍길동", "email": "hong@e.com", "password": "1234abcd" }
// 2. 로그인 → 토큰 받기
POST http://localhost:3000/api/auth/login
Body: { "email": "hong@e.com", "password": "1234abcd" }
Response: { "accessToken": "eyJ..." }
// 3. 게시글 작성 (토큰 필요)
POST http://localhost:3000/api/posts
Headers: Authorization: Bearer eyJ...
Body: { "title": "첫 번째 게시글", "content": "내용입니다." }
// 4. 게시글 목록 조회
GET http://localhost:3000/api/posts?page=1&limit=5
📚 현업 필수 추가 개념들
① 파일 업로드 (Multer)
npm install multer
const multer = require('multer');
const storage = multer.diskStorage({
destination: './uploads/',
filename: (req, file, cb) => {
cb(null, Date.now() + '-' + file.originalname);
},
});
const upload = multer({ storage, limits: { fileSize: 5 * 1024 * 1024 } }); // 5MB 제한
router.post('/upload', authMiddleware, upload.single('image'), (req, res) => {
res.json({ filename: req.file.filename, url: '/uploads/' + req.file.filename });
});
② 이메일 전송 (Nodemailer)
npm install nodemailer
const nodemailer = require('nodemailer');
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: { user: process.env.EMAIL, pass: process.env.EMAIL_PASS },
});
async function sendVerificationEmail(to, code) {
await transporter.sendMail({
from: '"My App" <noreply@myapp.com>',
to,
subject: '이메일 인증 코드',
html: '
인증 코드: ' + code + '
10분 내에 입력해주세요.
',
});
}
③ 환경변수 관리
// .env — 절대 GitHub에 올리지 않기!
DATABASE_URL="mysql://..."
JWT_SECRET="..."
// .env.example — 팀원에게 어떤 변수 필요한지 알려주는 파일 (GitHub에 올려도 OK)
DATABASE_URL="mysql://user:password@localhost:3306/dbname"
JWT_SECRET="your-secret-here"
PORT=3000
④ 에러 처리 패턴 (현업 표준)
// utils/ApiError.js
class ApiError extends Error {
constructor(statusCode, message) {
super(message);
this.statusCode = statusCode;
}
}
// 사용
router.get('/:id', async (req, res, next) => {
try {
const user = await prisma.user.findUnique({ where: { id: Number(req.params.id) } });
if (!user) throw new ApiError(404, '사용자를 찾을 수 없습니다.');
res.json(user);
} catch (err) {
next(err); // 전역 에러 핸들러로 넘기기
}
});
// 전역 에러 핸들러 (app.js 맨 아래)
app.use((err, req, res, next) => {
const status = err.statusCode || 500;
res.status(status).json({ error: err.message || '서버 오류' });
});
✅ 백엔드 학습 체크리스트
✅ 기초 (반드시 마스터)
☐ HTTP 요청/응답 개념 이해
☐ Node.js 설치 및 기본 모듈 사용
☐ Express로 CRUD API 구현
☐ SQL 기본 명령어 (SELECT, INSERT, UPDATE, DELETE)
☐ Prisma ORM으로 DB 연결
☐ JWT 로그인/로그아웃 구현
🚀 심화 (현업 수준)
☐ 미들웨어 직접 작성 (인증, 로깅, 에러 처리)
☐ REST API 설계 원칙 준수
☐ 비밀번호 bcrypt 해시화
☐ 파일 업로드 (Multer)
☐ Rate Limiting, Helmet 보안 설정
☐ Railway/Render로 배포
🏆 실전 미션
☐ 게시판 API (회원가입, 로그인, CRUD)
☐ Postman으로 전체 API 테스트
☐ 프론트엔드(React)와 연결하여 풀스택 앱 완성
🎯 다음 단계
백엔드 기초를 마쳤다면 다음을 추천합니다:
• TypeScript — 타입 안전성으로 대규모 프로젝트 관리
• NestJS — 현업에서 선호하는 구조화된 Node.js 프레임워크
• Docker — 컨테이너로 어디서나 동일하게 실행
• Redis — 캐시/세션/실시간 기능
• AWS — 클라우드 인프라 (EC2, RDS, S3)
반응형