이 글은 앞선 개념 정리(1편)를 바탕으로 실제로 Next.js, PostgreSQL, Docker를 활용하여 사용자 인증 시스템을 구축한 과정을 정리한 실습 후기입니다.
[데이터 저장 방식 1편] 사용자 인증 시스템 제대로 이해하기 : 세션, 암호화, 쿠키, PostgreSQL 기초
이 글은 "데이터 저장 방식 구현 과제"를 수행하기 전, 인증 시스템을 설계하기 위해 반드시 이해해야 할 주요 개념들을 정리한 글입니다. 프론트엔드부터 백엔드, 그리고 보안 요소까지 아우르
neuri.tistory.com
1. 프로젝트 개요 및 구조
🛠 기술 스택
- Frontend : Next.js14, Typescript
- Backend : Node.js, PostreSQL
- Infra : Docker,docker-compose
- 암호화 : SHA256(단방향 해시) + RSA(양방향 비대칭키 암호화)
주요 흐름 요약
1. 클라이언트 → 로그인 정보 입력
2. 비밀번호 → SHA256 해시 → RSA 공개키로 암호화
3. 서버에서 RSA 개인키로 복호화
4. PostgreSQL의 해시값과 비교 → 인증 성공
5. 세션 생성 → 쿠키 반환 (HttpOnly, Secure 포함)
2. DB 설정 및 사용자 테이블 구성
PostgreSQL, DBeaver 설치 및 기초 설정
1. PostgreSQL 다운로드 합니다.
OS에 맞는 최신버전을 설치해주세요.
https://www.enterprisedb.com/downloads/postgres-postgresql-downloads
EDB: Open-Source, Enterprise Postgres Database Management
www.enterprisedb.com
비밀번호 : 0000
PostgreSQL 자체의 관리자 계정에 대한 인증용입니다. DB 서버 내부에서 인증할 때 사용하며, SQL 클라이언트나 애플리케이션 DB 연결할 때 인증 수단이 됩니다.
기본 포트 : 5432
Locale : Korean (Korea)
이후 Next> 를 클릭해주세요. Finish 버튼이 활성화 되었을 때 체크박스를 해제 후 클릭해주세요.
pgAdmin4를 실행해줍니다. 이후 비밀번호를 입력한 뒤 OK 를 클릭합니다.
만든 서버에서 오른쪽 마우스 클릭 후 Create - Database를 클릭합니다.
원하는 이름으로 설정 후 Save를 클릭합니다.
2. DBeaver 설치 및 연결
자신의 OS에 맞는 DBeaver를 설치합니다.
Download | DBeaver Community
Download DBeaver Community 25.0.5 Released on May 18th 2025 (Milestones). It is free and open source (license). Also you can get it from the GitHub mirror. System requirements. DBeaver PRO 25.0 Released on March 10th, 2025 PRO version website: dbeaver.com
dbeaver.io
3. user Table 생성 및 유저 정보 추가하기
CREATE TABLE users (
no SERIAL PRIMARY KEY,
email VARCHAR NOT NULL, -- 닉네임
id VARCHAR NOT NULL UNIQUE, -- 아이디
password VARCHAR NOT NULL -- SHA2 암호화된 비밀번호
);
INSERT INTO users (email , id, password)
VALUES (
'test@naver.com',
'ohanul98',
encode(digest('1234', 'sha256'), 'hex') -- SHA256 해시 후 HEX 문자열로 변환
);
3. 프론트엔드 구성
로그인 폼 (Client Component)
- 로그인, 로그아웃 UI 생성
- 버튼 클릭 시 사용자 입력을 받아 서버로 전송
- 입력된 비밀번호는 클라이언트에서 SHA256 해시 암호화 적용 후 전송
클라이언트 스토리지 활용
- sessionStorage에 비민감데이터를 자정하여 탭간 공유 제한 확인
- localStorage를 사용하여 사용자 설정(테마)를 저장
4. 서버 층 인증 처리
1. DB 연결
postgre 클라이언트 라이브러리 설치
npm install pg
npm i --save-dev @types/pg // typescript
// lib/db.ts
import { Pool } from "pg";
const pool = new Pool({
user: "postgres",
host: "localhost",
database: "mydb", // 확인 !!
password: "0000", // 확인!!
port: 5432,
});
export default pool;
여기서 잠깐, 서버에서 직접 DB에 접속하지 않고 DB 클라이언트를 사용해야 하나요?
- 통신 규약이 다름
- PostgreSQL, Redis, MongoDB는 각각 고유한 통신 프로토콜을 사용함
- Node.js는 이러한 프로토콜을 "직접" 이해하지 못함
- 따라서 중간 다리 역할을 해주는 라이브러리가 필요함
- 클라이언트가 연결 관리, 에러 처리, 보안 설정 등을 도와줌
- 안정적인 운영에 필수
💡Node.js(한국어)가 PostgreSQL(프랑스어)와 대화하려면 pg(통역사)가 필요하다
2. API 엔드포인트 : /api/auth/login
- 요청 본문에서 복호화한 해시값을 DB의 SHA256 해시값과 비교
- 일치 시 성공 → 세션 생성 + Set-Cookie 반환
import { NextRequest, NextResponse } from "next/server";
import pool from "@/app/lib/postgre";
import { decryptWithPrivateKey } from "@/app/lib/rsaCrypto";
export async function POST(request: NextRequest) {
const body = await request.json();
const { email, encryptedPassword } = body;
console.log("-------사용자의 로그인 요청 정보--------");
console.log("email:", email);
console.log("password:", encryptedPassword);
try {
// PostgreSQL 연결
// 쿼리 실행
const query = `SELECT * FROM users WHERE email = $1 AND password = $2`;
const privateKey = process.env.RSA_PRIVATE_KEY?.replace(/\\n/g, "\n") || "";
// 비밀번호 복호화
const password = decryptWithPrivateKey(privateKey, encryptedPassword);
console.log("password 개인키 복호화" + password);
const values = [email, password];
const result = await pool.query(query, values);
// 결과 확인
console.log(result.rows);
// 로그인 성공 여부에 따라 응답 반환
if (result.rows.length > 0) {
const response = NextResponse.json({ success: true });
// 쿠키 값 (예 : 세션 ID)
const sessionToken = "session_token";
// Set-Cookie 헤더 설정
// HttpOnly, Secure, SameSite 설정
response.cookies.set("session", sessionToken, {
name: "session",
value: sessionToken,
httpOnly: true,
secure: true,
maxAge: 180,
sameSite: "strict",
});
return response;
} else {
return NextResponse.json({ success: false });
}
} catch (e) {
// 에러 처리
console.error("DB Error:", e);
return NextResponse.json(
{ success: false, error: "Database error" },
{ status: 500 }
);
}
}
5. Docker 구성 및 통합
1. 각각을 독립된 컨테이너로 패키징하고 네트워크로 연결하기
- Next.js : 직접 Dockerfile 작성
- PostgreSQL(DB서버)는 Docker Hub의 공식 이미지 활용
- DB 서버는 공식 이미지가 잘 구성되어 있어 직접 이미지를 만들 필요가 없다. 환경 변수 설정만으로 바로 실행할 수 있어서 편리하다.
2. 컨테이너들을 docker-compose로 함께 실행하기
- 모든 컨테이너를 하나의 docker-compose.yml 파일로 통합 관리
- 개별적으로 docker run 명령어로 실행할 수도 있지만, 의존성과 네트워크 구성, 환경 변수 설정이 복잡하다. docker-compose를 사용하면 모든 설정을 한 파일에서 관리할 수 있고, 컨테이너 간 네트워크도 자동으로 연결되어 운영이 훨씬 수월하다.
docker-compose.yml 파일 작성
보통 프로젝트 루드 디렉터리에 작성한다.
주의할 점 : DATABASE_URL 설정에서 호스트 이름을 localhost 로 설정하면 안된다. 컨테이너 간에는 localhost가 아닌 컨테이너 이름을 통해 통신해야하기 때문이다.
project-root/
├── docker-compose.yml
├── next-app/
│ ├── Dockerfile
│ └── ... (Next.js 코드)
version: "3.8"
services:
nextjs:
build:
context: . // Next.js 프로젝트 경로
container_name: nextjs-app
ports:
- "3000:3000"
depends_on:
- postgres
environment:
DATABASE_URL: postgres://postgres:0000@postgres:5432/mydb
networks:
- app-network
postgres:
image: postgres:15
container_name: postgres-db
restart: always
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: 0000
POSTGRES_DB: mydb
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
networks:
- app-network
volumes:
pgdata:
networks:
app-network:
6. 마무리 및 회고
이번 포스팅에서는 Next.js와 PostgreSQL을 기반으로 한 로그인 시스템을 직접 구현하면서, 사용자 인증 흐름 전반에 대한 실질적인 이해를 얻을 수 있었습니다. 환경 구성 측면에서는 Docker와 docker-compose를 활용하여 프론트엔드, 백엔드, 데이터베이스를 하나의 환경으로 통합하였고, 이 과정에서 컨테이너 간 통신 문제, 초기 SQL 자동 실행, 환경 변수 구성 등 실무에서 자주 마주칠 수 있는 이슈들을 경험하고 해결해 볼 수 있었습니다.
다음 포스팅에서는 이번 구현에서 이어지는 내용으로, RSA를 중심으로 한 양방향 암호화를 프로젝트에 적용해보며 관련 내용을 다룰 예정입니다.
'📂 BE' 카테고리의 다른 글
[데이터 저장 방식 1편] 사용자 인증 시스템 제대로 이해하기 : 세션, 암호화, 쿠키, PostgreSQL 기초 정리 (2) | 2025.05.22 |
---|---|
[Docker] Docker 기초 개념 및 핵심 정리 (0) | 2025.05.19 |