PostgreSQL
강의

#3 - WSL에서 시작하는 실전 PostgreSQL 17 + Redis 개발환경 구축, 강의용 데이터 구축

중년개발자
중년개발자

@loxo

22일 전

31

Next.js, Spring Boot 강의에 필요한 데이터베이스 구축 및 샘플 데이터입니다.
중간중간 설치 시 에러가 있을 수 있습니다. 각자의 환경이 제각각이라서 그럴 땐 AI에게 질문하셔서 끝까지 설치 부탁드립니다.

WSL 개발환경 구축 가이드 (Debian + PostgreSQL 17 + Redis)

WSL 설치부터 PostgreSQL/Redis 구성, 소유자 계정과 애플리케이션 계정 분리, 파티션 설계, 대량 샘플 데이터까지 포함합니다.

⚠️ 이 문서는 *그대로 따라 쳐도 오류 없이 완주**하도록 설계되었습니다.


0. 전체 구조 요약 (먼저 읽기)

역할 분리 원칙 (중요)

계정역할
postgresDBMS 슈퍼유저 (설치/관리 전용)
lecture_ownDB/Schema/Table 소유자
lecture_appSpring Boot 등 애플리케이션 전용 계정

Tablespace 분리

용도Tablespace
데이터lecture_data_ts
인덱스lecture_index_ts

1. WSL 설치 (처음부터 시작)

1-1. Windows PowerShell (관리자)

powershell
wsl --install -d debian

이미 WSL이 있다면 무시됨

설치 후 재부팅 → Debian 실행

💡 Windows에서 Debian(WSL) 실행 방법 보충 설명

  1. Win 키 + XWindows 터미널 선택

    • 관리자 권한이 필요한 경우: Windows 터미널(관리자)
  2. 터미널 상단의 버튼 클릭

  3. 목록에서 Debian 선택

또는 터미널에서 바로 실행:

powershell
wsl -d debian

👉 강의 중에는 Win + X → Windows 터미널 방식을 권장 (PowerShell / WSL / 여러 리눅스를 빠르게 전환 가능)


2. Debian 기본 설정

bash
sudo apt update && sudo apt upgrade -y sudo apt install -y curl wget gnupg ca-certificates lsb-release uuid-runtime

3. PostgreSQL 17 설치 (Debian 기본 권장)

3-1. 버전 확인

bash
apt show postgresql

Debian 최신 배포판 기준 PostgreSQL 17

3-2. 설치

bash
sudo apt install -y postgresql postgresql-contrib

확인:

bash
psql --version

3-3. pgroonga 패키지 저장소 추가 및 설치 (PostgreSQL 17 기준)

⚠️ 중요

  • pgroonga는 PostgreSQL 메이저 버전과 정확히 매칭되어야 함
  • apt show postgresql 버전과 일치하는 pgroonga 패키지 설치
  • postgresql-17-pgroonga ← PostgreSQL 17 전용 패키지
  • 버전을 명시하지 않으면 다른 버전이 설치되어 extension 생성 실패 가능
  • debian 용
bash
sudo mkdir -p /etc/apt/keyrings wget -qO- https://packages.groonga.org/debian/groonga-archive-keyring.gpg \ | sudo tee /etc/apt/keyrings/groonga.gpg > /dev/null echo "deb [signed-by=/etc/apt/keyrings/groonga.gpg] \ https://packages.groonga.org/debian/ \ $(lsb_release -cs) main" \ | sudo tee /etc/apt/sources.list.d/groonga.list sudo apt update sudo apt install -y postgresql-17-pgroonga
  • 참고:ubuntu 용
bash
sudo apt install -y software-properties-common sudo add-apt-repository ppa:groonga/ppa sudo apt update sudo apt install -y postgresql-17-pgroonga

4. 접근 제어 설정 (PostgreSQL 설치 직후)

⚠️ PostgreSQL 설치 후 바로 설정해야 이후 접속 시 보안이 적용됩니다.

4-1. pg_hba.conf 수정

💡 업계 표준 설정

  • local + peer: postgres 슈퍼유저는 OS 계정으로 인증 (sudo -i -u postgres)
  • host + scram-sha-256: 애플리케이션 계정은 TCP/IP로 비밀번호 인증
bash
sudo vi /etc/postgresql/17/main/pg_hba.conf
conf
# TYPE DATABASE USER ADDRESS METHOD # Unix socket: postgres 관리자는 peer (OS 사용자 인증) local all postgres peer # TCP/IP: 애플리케이션 계정은 host + scram-sha-256 host all postgres 127.0.0.1/32 scram-sha-256 host lecture_db lecture_own 127.0.0.1/32 scram-sha-256 host lecture_db lecture_app 127.0.0.1/32 scram-sha-256 # IPv6 localhost host lecture_db lecture_own ::1/128 scram-sha-256 host lecture_db lecture_app ::1/128 scram-sha-256

4-2. postgresql.conf 수정

bash
sudo vi /etc/postgresql/17/main/postgresql.conf
conf
listen_addresses = 'localhost' shared_buffers = 512MB max_connections = 200

4-3. PostgreSQL 재시작

bash
sudo systemctl restart postgresql

5. Tablespace 물리 폴더 생성 (로그인 사용자)

⚠️ 중요: 이 단계는 postgres 계정이 아닌 로그인 사용자sudo로 실행합니다.

bash
sudo mkdir -p /data/pgdata /data/pgindex sudo chown postgres:postgres /data/pgdata /data/pgindex sudo chmod 700 /data/pgdata /data/pgindex

6. PostgreSQL 초기 설정 (postgres 계정)

여기서부터 postgres 계정으로 작업합니다.

bash
sudo -i -u postgres psql

6-1. 기본 보안

sql
ALTER USER postgres WITH PASSWORD 'strong_postgres_password';

7. Tablespace 생성 (postgres 실행)

psql 접속 상태에서 실행

sql
CREATE TABLESPACE lecture_data_ts LOCATION '/data/pgdata'; CREATE TABLESPACE lecture_index_ts LOCATION '/data/pgindex';

8. Role / Database 생성 (postgres 실행)

sql
CREATE ROLE lecture_own LOGIN PASSWORD 'lecture_own_pw'; CREATE ROLE lecture_app LOGIN PASSWORD 'lecture_app_pw'; CREATE DATABASE lecture_db OWNER lecture_own TABLESPACE lecture_data_ts ENCODING 'UTF8'; -- public 스키마 기본 권한 제거 (보안 기본값) REVOKE ALL ON SCHEMA public FROM PUBLIC; REVOKE ALL ON DATABASE lecture_db FROM PUBLIC; -- extension 사용을 위한 public 스키마 접근은 최소한으로 재허용 GRANT USAGE ON SCHEMA public TO lecture_own; GRANT USAGE ON SCHEMA public TO lecture_app; GRANT CONNECT ON DATABASE lecture_db TO lecture_app; -- lecture_own에게 데이터베이스와 테이블스페이스에 대한 모든 권한 부여 GRANT ALL PRIVILEGES ON DATABASE lecture_db TO lecture_own; GRANT CREATE ON TABLESPACE lecture_data_ts TO lecture_own; GRANT CREATE ON TABLESPACE lecture_index_ts TO lecture_own; -- 소유자 기본 search_path 설정 (extension → public, 도메인 → board) ALTER ROLE lecture_own SET search_path = board, public; ALTER ROLE lecture_app SET search_path = board, public; -- 슈퍼유저(postgres) 권한이 필요한 extension 설치 \c lecture_db CREATE EXTENSION IF NOT EXISTS "pgroonga"; CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; CREATE EXTENSION IF NOT EXISTS "pgcrypto";

psql 종료 후 postgres 계정에서 나옵니다:

sql
\q
bash
exit

9. Schema 및 권한 (lecture_own 실행)

bash
psql -U lecture_own -d lecture_db -h localhost
sql
CREATE SCHEMA board AUTHORIZATION lecture_own; -- Manage Application User Permissions GRANT USAGE ON SCHEMA board TO lecture_app; -- Set DEFAULT PRIVILEGES ALTER DEFAULT PRIVILEGES FOR ROLE lecture_own IN SCHEMA board GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO lecture_app; ALTER DEFAULT PRIVILEGES FOR ROLE lecture_own IN SCHEMA board GRANT USAGE, SELECT ON SEQUENCES TO lecture_app; -- public 스키마에 생성되는 객체 기본 권한 통제 ALTER DEFAULT PRIVILEGES FOR ROLE lecture_own IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO lecture_app; ALTER DEFAULT PRIVILEGES FOR ROLE lecture_own IN SCHEMA public GRANT USAGE, SELECT ON SEQUENCES TO lecture_app;

10. 사용자(users) 테이블 – Google OAuth 기본 설계

실무 기준 OAuth 사용자 테이블

  • Google OAuth 전용 (확장 가능)
  • UUID v7 기반 생성 시점 정렬
  • 게시글과 FK 연결
  • 사용자 삭제 시 게시글은 논리적 보호 (facade)

10-1. UUID v7 생성 함수

PostgreSQL 17 기준 (시간 정렬 UUID)
PostgreSQL 18 에는 uuidV7 함수 내장으로 탑재 되어 있습니다.
SELECT uuidv7();

sql
CREATE EXTENSION IF NOT EXISTS pgcrypto; -- UUIDv7 Generation Function CREATE OR REPLACE FUNCTION uuidv7() RETURNS uuid AS $$ DECLARE timestamp_ms bigint; unix_ts_ms bytea; uuid_bytes bytea; BEGIN timestamp_ms := (extract(epoch from clock_timestamp()) * 1000)::bigint; unix_ts_ms := substring(int8send(timestamp_ms) from 3); uuid_bytes := unix_ts_ms || gen_random_bytes(10); uuid_bytes := set_byte(uuid_bytes, 6, (get_byte(uuid_bytes, 6) & x'0f'::int) | x'70'::int); uuid_bytes := set_byte(uuid_bytes, 8, (get_byte(uuid_bytes, 8) & x'3f'::int) | x'80'::int); RETURN encode(uuid_bytes, 'hex')::uuid; END $$ LANGUAGE plpgsql VOLATILE;

10-2. users 테이블

sql
CREATE TABLE board.users ( user_id UUID PRIMARY KEY DEFAULT uuidv7(), provider VARCHAR(20) NOT NULL, -- google provider_id VARCHAR(100) NOT NULL, -- sub email VARCHAR(255) NOT NULL, name VARCHAR(100), picture_url TEXT, created_at TIMESTAMP NOT NULL DEFAULT now(), UNIQUE (provider, provider_id) ) TABLESPACE lecture_data_ts;

10-3. OAuth / 조회 최적화 인덱스

sql
CREATE UNIQUE INDEX idx_users_provider ON board.users (provider, provider_id) TABLESPACE lecture_index_ts; CREATE INDEX idx_users_email ON board.users (email) TABLESPACE lecture_index_ts; CREATE INDEX idx_users_created_desc ON board.users (user_id DESC) TABLESPACE lecture_index_ts;

11. 게시판 테이블 (users 연관, schema.sql 기준)

11-1. 부모 테이블 (HASH 파티션)

sql
CREATE TABLE board.posts ( post_id UUID NOT NULL DEFAULT uuidv7(), user_id UUID NOT NULL, title VARCHAR(200) NOT NULL, content TEXT NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT now(), PRIMARY KEY (post_id) ) PARTITION BY HASH (post_id); -- HASH 파티션 생성 (예: 4개 버킷) CREATE TABLE board.posts_p0 PARTITION OF board.posts FOR VALUES WITH (MODULUS 4, REMAINDER 0) TABLESPACE lecture_data_ts; CREATE TABLE board.posts_p1 PARTITION OF board.posts FOR VALUES WITH (MODULUS 4, REMAINDER 1) TABLESPACE lecture_data_ts; CREATE TABLE board.posts_p2 PARTITION OF board.posts FOR VALUES WITH (MODULUS 4, REMAINDER 2) TABLESPACE lecture_data_ts; CREATE TABLE board.posts_p3 PARTITION OF board.posts FOR VALUES WITH (MODULUS 4, REMAINDER 3) TABLESPACE lecture_data_ts;

11-2. FK + CASCADE DELETE (탈퇴 시 즉시 삭제)

sql
ALTER TABLE board.posts ADD CONSTRAINT fk_posts_user FOREIGN KEY (user_id) REFERENCES board.users (user_id) ON DELETE CASCADE;

❗ 사용자 탈퇴 정책 (가정):

  • users 레코드는 즉시 DELETE
  • 해당 사용자의 posts는 DB 레벨에서 자동 삭제
  • 애플리케이션 레이어에서 별도 정합성 처리 불필요
  • 탈퇴 = 데이터 완전 제거 (개인정보/게시글 모두 삭제)

12. 검색 전용 테이블 (posts 연관)

sql
-- 🔍 검색 전용 테이블 (쓰기/조회 분리 원칙) -- posts 와 1:1 관계, 검색 컬럼만 유지 CREATE TABLE board.post_search ( post_id UUID NOT NULL, search_text TEXT NOT NULL, PRIMARY KEY (post_id) ) PARTITION BY HASH (post_id); -- HASH 파티션 (posts 와 동일한 버킷 전략) CREATE TABLE board.post_search_p0 PARTITION OF board.post_search FOR VALUES WITH (MODULUS 4, REMAINDER 0) TABLESPACE lecture_data_ts; CREATE TABLE board.post_search_p1 PARTITION OF board.post_search FOR VALUES WITH (MODULUS 4, REMAINDER 1) TABLESPACE lecture_data_ts; CREATE TABLE board.post_search_p2 PARTITION OF board.post_search FOR VALUES WITH (MODULUS 4, REMAINDER 2) TABLESPACE lecture_data_ts; CREATE TABLE board.post_search_p3 PARTITION OF board.post_search FOR VALUES WITH (MODULUS 4, REMAINDER 3) TABLESPACE lecture_data_ts; -- pgroonga 인덱스 (파티션 전체에 자동 적용) CREATE INDEX idx_post_search_text ON board.post_search USING pgroonga (search_text) TABLESPACE lecture_index_ts;

13. 대량 샘플 데이터 생성 (무한 스크롤용)

13-1. users 샘플 데이터 생성 (Google OAuth 가정)

강의용 사용자 더미 데이터

  • 실제 Google OAuth 구조와 동일
  • provider + provider_id 유니크 보장
  • 게시글 FK 테스트용
sql
INSERT INTO board.users (provider, provider_id, email, name, picture_url) SELECT 'google', 'google-sub-' || gs, 'user' || gs || '@example.com', '강의용 사용자 ' || gs, 'https://picsum.photos/seed/' || gs || '/200/200' FROM generate_series(1, 100) gs;

13-2. posts 샘플 데이터 생성 (무한 스크롤용)

sql
INSERT INTO board.posts (post_id, user_id, title, content, created_at) SELECT uuidv7(), u.user_id, '강의용 게시글 ' || gs, '이것은 무한 스크롤 테스트를 위한 본문 데이터 ' || gs, now() - (gs || ' seconds')::interval FROM generate_series(1, 20000) gs CROSS JOIN LATERAL ( SELECT user_id FROM board.users ORDER BY random() LIMIT 1 ) u;

13-3. post_search 샘플 데이터 동기화

posts 데이터를 기준으로 검색 전용 테이블 동기화 (실무에서는 트리거 또는 애플리케이션 레이어에서 처리)

sql
INSERT INTO board.post_search (post_id, search_text) SELECT p.post_id, u.name || ' ' || p.title || ' ' || p.content FROM board.posts p INNER JOIN board.users u ON p.user_id = u.user_id;

14. Redis 설치 및 보안

bash
sudo apt install -y redis-server sudo vi /etc/redis/redis.conf
conf
requirepass strong_redis_password bind 127.0.0.1 protected-mode yes
bash
sudo systemctl restart redis-server

15. 이 상태에서 가능한 강의

  • Spring Boot + PostgreSQL 17 연동
  • UUID + HASH 파티션 전략
  • pgroonga 전문 검색
  • Redis 캐시 & 무한 스크롤 API
  • 실무 권한 분리 구조 설명

다음과 같이 되어 있어야 합니다.
clipboard-1769154793664.png
clipboard-1769154885103.png

목차

#WSL#PostgreSQL#Redis#개발환경 구축#postgresql 18 uuidv7

댓글 0

Ctrl + Enter를 눌러 등록할 수 있습니다
※ AI 다듬기는 내용을 정제하는 보조 기능이며, 최종 내용은 사용자가 확인해야 합니다.