본문 바로가기
프로그래밍

DB 설계 :: DBSM :: SQL -> Query :: CRUD

by 로맨틱스터디 2025. 10. 10.
728x90
반응형

API 명세 확정 후 DB 설계(데이터 구조 설계) 단계는 백엔드 품질과 확장성을 좌우하는 핵심이죠.
아래는 실무 기준의 단계별 접근법입니다.


💡 1. API 명세서로부터 요구사항 추출

API 명세가 확정됐다면, 이미 아래 정보가 들어있을 겁니다:

  • 엔드포인트 (ex: POST /users, GET /articles/{id})
  • 요청/응답 스키마 (request body, response JSON 구조)
  • 리소스 간 관계 (ex: 유저 → 게시글 → 댓글)

👉 이 단계의 목표:
“서버가 관리해야 하는 데이터종류/관계”를 파악하는 것.

 

예:

{
  "user": {
    "id": 1,
    "name": "Alice"
  },
  "posts": [
    {
      "id": 10,
      "title": "Hello",
      "authorId": 1
    }
  ]
}

 

→ 테이블 후보: users, posts
→ 관계: posts.authorId → users.id (1:N 관계)

 

Q. 경로 변수 {id}

더보기

GET /articles/{id} 에서 {id} 는 경로 변수(Path Parameter) 라고 합니다.
즉, URL 안에 변하는 값(식별자) 을 담는 플레이스홀더(placeholder) 에요.


🧩 예시로 설명해볼게요

요청 의미
GET /articles/1 ID가 1인 게시글 가져오기
GET /articles/42 ID가 42인 게시글 가져오기

이때 API 명세서에서는 {id}처럼 중괄호로 감싸서 표현합니다.
→ “이 부분은 실제로는 숫자나 문자열 값으로 대체될 거야” 라는 뜻이에요.


📘 용어 정리

구분 예시 설명
Path Parameter (경로 변수) /articles/{id} 리소스의 식별자, URL 경로에 포함됨
Query Parameter (쿼리 파라미터) /articles?author=kim&page=2 필터링, 정렬, 페이지네이션 등에 사용
Body Parameter (본문 데이터) { "title": "새 글", "body": "내용" } POST/PUT/PATCH 요청 시 전송되는 JSON 데이터

🧠 백엔드 코드 예시 (Node.js + Express)

app.get('/articles/:id', (req, res) => {
  const articleId = req.params.id; // ← 여기서 :id는 URL의 {id}에 대응
  // 예: GET /articles/5 → articleId = '5'
  
  // DB 조회 예시
  const article = findArticleById(articleId);
  res.json(article);
});

요약하자면

✅ {id}는 URL 안에서 리소스를 구분하기 위한 변수 자리(placeholder)
API 요청 시 실제 값으로 대체돼요.

 


 

Q. :id 란?

 

이 차이를 명확히 이해하면 API 명세서실제 코드 구현을 연결하기가 훨씬 쉬워집니다.


✅ 핵심 요약부터

표현 쓰이는 곳 의미
/articles/{id} API 명세서 (문서에서) “이 자리에 동적으로 변하는 값이 들어온다”는 표기 규칙
/articles/:id 백엔드 코드 (실제 서버 라우팅 코드, Express 등) 실제 서버 라우팅 코드에서 변수를 선언하는 문법

즉 —
의미(동적으로 변하는 값 = 변수)는 같지만, 쓰는 맥락이 다릅니다.


🔍 자세히 보면

📄 API 명세서에서 {id}

OpenAPI(Swagger)나 REST API 문서에서는 {} 중괄호로 “이건 변수야”라고 표시합니다.

예:

 
GET /articles/{id}

→ “URL의 {id} 자리에 실제 글 번호가 들어온다”는 뜻.

 

요청 예시:

 
GET /articles/1 
GET /articles/123

⚙️ 서버 코드에서 :id

Express, Fastify, NestJS 등 대부분의 Node.js 프레임워크에서는
라우트 경로 안에서 콜론(:) 을 사용해 “이 부분은 변수야” 라고 정의합니다.

 
app.get('/articles/:id', (req, res) => {
  console.log(req.params.id);
});

요청:

 
GET /articles/5

→ req.params.id === '5'


🔄 요약 비교

구분 명세서예시 코드에서 실제 요청
문법 /articles/{id} /articles/:id /articles/10
목적 API 문서용 표기 서버 라우팅용 문법 클라이언트가 호출
역할 변수 위치 표시 변수 값 추출 실제 값 전달됨

💡 비유로 쉽게 말하면

구분 비유
/articles/{id} “요리책에서 {재료} 준비하기” — 문서용 표기
/articles/:id “레시피 코드에서 :ingredient 받아오기” — 실제 구현
/articles/5 감자를 실제로 넣는 행위” — 실제 요청

 


Q. req.params.id === 5

 

req.params.id === 5 안에는 JavaScript 문법Express의 개념이 같이 들어 있습니다.


💡 1️⃣ req는 뭐냐면

req는 request 객체예요.
즉, 클라이언트가 보낸 요청(Request) 의 모든 정보를 담고 있는 객체입니다.

 

서버 코드 예시:

 
app.get('/articles/:id', (req, res) => {
  console.log(req); // 클라이언트 요청에 대한 모든 정보
});

 

req 안에는 여러 종류의 데이터가 들어 있습니다 👇

속성 설명 예시
req.params URL 경로 변수 /articles/:id 에서 :id
req.query 쿼리스트링 /articles?id=5&page=2
req.body POST/PUT의 JSON 데이터 { "title": "Hello" }
req.headers 요청 헤더들 Authorization, Content-Type

💡 2️⃣ params는 뭐냐면

params는 path parameter(경로 변수)들을 담고 있는 **객체(Object)**입니다.

app.get('/articles/:id')

 

예를 들어 이런 요청이 들어오면:

 
GET /articles/5

 

라우터가 이렇게 되어 있다면:

 
app.get('/articles/:id', (req, res) => {
  console.log(req.params);
});

 

출력 결과:

 
{ id: '5' }

즉,

  • req.params → { id: '5' } (params = 객체 => 객체로 반환)
  • req.params.id → '5' (문자열 타입)

💡 3️⃣ ===는 뭐냐면

===는 자바스크립트의 “엄격한 비교 연산자(strict equality operator)” 입니다.

연산자 이름 설명
== 느슨한 비교(loose equality) 타입을 자동으로 변환해서 비교
=== 엄격한 비교(strict equality) 타입이 모두 같아야 true

예시:

 
'5' == 5   // true  (문자열이 자동으로 숫자로 변환)
'5' === 5  // false (타입이 다름: string vs number)
5 === 5    // true

💡 4️⃣ 그래서 req.params.id === 5는…

이건 “URL 경로에서 받은 id가 숫자 5와 같은가?”를 확인하는 코드예요.
그런데! req.params.id문자열이기 때문에 ===로 비교하면 false가 됩니다.

예:

 
req.params.id === 5   // false ('5' !== 5)
req.params.id == 5    // true  (자동 변환돼서)

실제로 비교하려면 타입 변환을 해주는 게 좋습니다:

 
Number(req.params.id) === 5   // true

✅ 정리

항목 설명 예시
req 요청(request) 객체 모든 요청 정보
req.params 경로 변수 객체 { id: '5' }
req.params.id '5' (문자열) 경로의 실제 값
=== 엄격한 비교 연산자 값 + 타입 모두 비교

 

 


 

Q. Number()

 

이제 JavaScript에서 아주 기본적이면서도 매우 자주 쓰이는 내장 함수 Number()에 대해 정리해볼게요.


💡 Number()란?

Number()는 JavaScript의 내장 함수로,
다른 자료형(문자열, 불리언 등) 을 -> 숫자(Number 타입) 으로 변환(conversion) 해주는 함수입니다.

즉,

👉 “이 값을 숫자로 바꿔줘”

라고 할 때 사용하는 거예요.


🧩 기본 문법

Number(값)

반환: 숫자형(Number)


🔍 예제 살펴보기

1️⃣ 문자열 → 숫자

Number('5')     // 5
Number('42.3')  // 42.3
Number('0')     // 0
Number('abc')   // NaN (숫자로 변환 불가)

📘 NaN = “Not a Number” (숫자가 아님을 의미하는 특별한 값)


2️⃣ 불리언 → 숫자 (1, 0)

Number(true)   // 1
Number(false)  // 0

3️⃣ null / undefined → 숫자 (0, NaN)

 
Number(null)       // 0
Number(undefined)  // NaN

4️⃣ 혼합 예시 (공백, ' ', '숫자+영어')

Number('   123   ')  // 123 (공백 무시)
Number('')           // 0
Number('12a')        // NaN

💡 실무에서 자주 쓰는 이유

예를 들어 Express에서:

 
app.get('/articles/:id', (req, res) => {
  console.log(typeof req.params.id); // 'string'
  
  const id = Number(req.params.id);  // 문자열 → 숫자 변환
  console.log(typeof id);            // 'number'
});

💬 즉,
URL에서 받은 값('5')은 문자열이라 숫자 비교나 계산을 하려면 Number()로 변환해야 합니다.


⚠️ 주의할 점

상황 결과 설명
Number('123abc') NaN 숫자+문자 섞이면 변환 실패
Number('') 0 빈 문자열은 0으로 처리
Number(true) 1 불리언은 1/0으로 변환
Number(undefined) NaN 변환 불가능

🧠 참고: 숫자로 바꾸는 다른 방법들

방법 예시 설명
Number() Number('5') 명시적 변환 (가장 안전)
parseInt() parseInt('5px') → 5 문자열에서 숫자 부분만 추출
+ (단항 연산자) +'5' → 5 짧게 쓰는 방법
parseFloat() parseFloat('3.14') → 3.14 소수점 포함 숫자 변환

요약

  • Number()는 값을 숫자형으로 변환하는 내장 함수
  • 변환이 불가능하면 NaN 반환
  • 문자열로 받은 숫자('5')를 실제 숫자(5)로 바꾸는 데 자주 사용됨

 

Q. 스키마(Schema)?

더보기

이 질문은 API를 설계하거나 백엔드를 구현할 때 핵심 개념을 정확히 짚은 거예요.
이제 “스키마(schema)”가 무엇을 의미하는지 쉽게, 그리고 실무 관점에서 정리해볼게요.


💡 한 줄 정의

스키마(Schema) = 데이터의 구조(Structure) 를 정의한 “설계도

즉, “이 API에서 어떤 데이터가 오고, 어떤 데이터가 나가는지”를 명확히 표현한 형식이에요.


🧩 요청(Request) 스키마 vs 응답(Response) 스키마

구분 설명 예시
요청 스키마 (Request Schema) 클라이언트가 서버로 보내는 데이터 구조 회원가입 시 body에 포함되는 JSON
응답 스키마 (Response Schema) 서버가 클라이언트에게 돌려주는 데이터 구조 등록된 유저 정보 JSON

📘 예시로 이해하기

🔸 API 명세

POST /users

🔹 요청(Request) 스키마

사용자가 회원가입할 때 보내는 데이터 구조

 
{
  "name": "Alice",
  "email": "alice@example.com",
  "password": "1234"
}

➡️ 이게 “요청 스키마”예요.
서버는 이 구조에 맞는 데이터를 기대합니다.


🔹 응답(Response) 스키마

서버가 회원가입이 끝난 뒤 돌려주는 데이터 구조

 
{
  "id": 1,
  "name": "Alice",
  "email": "alice@example.com",
  "created_at": "2025-10-09T12:34:56Z"
}

➡️ 이게 “응답 스키마”예요.
서버가 반환할 JSON 구조약속하는 거죠.


🧠 비유로 이해하기

개념 비유
요청 스키마 “요리 재료 목록” — 요리를 시작하려면 재료가 맞아야
응답 스키마 “요리 결과물의 형태” — 만들어서 돌려주는 완성품 형태
스키마 자체 “요리 레시피의 설계도” — 어떤 재료순서로 구성되는지 설명

⚙️ 실무에서는 이렇게 사용돼요

예를 들어 OpenAPI (Swagger) 문서에서는 이런 식으로 정의합니다 👇

 
paths:
  /users:
    post:
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                email:
                  type: string
                password:
                  type: string
              required: [name, email, password]
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: integer
                  name:
                    type: string
                  email:
                    type: string

💬 여기서 schema가 바로 JSON 데이터 구조를 정의하는 부분이에요.


✅ 요약 정리

구분의미예시
구분 의미 예시
스키마(schema) 데이터의 구조/형식/규칙 정의 JSON, DB, API 등 어디서든 사용
요청 스키마 클라이언트 → 서버로 보내는 데이터 구조 { "name": "Alice" }
응답 스키마 서버 → 클라이언트로 보내는 데이터 구조 { "id": 1, "name": "Alice" }

 


 

Q. type: integer (responses - id)

 

type: integer에서 integer(인트저) 가 뭔지 이해하면, 데이터 타입(type) 개념이 완전히 잡힙니다.


💡 한 줄 정의

integer(정수) = 소수점이 없는 숫자
예: -3, 0, 42, 99999


📘 예시로 비교해볼게요

타입 설명
42 ✅ integer 정수 (소수점 없음)
3.14 number (실수) 소수점 있음
-10 ✅ integer 정수
"42" string 문자열, 숫자가 아님
0 integer 정수

🧩 스키마에서의 의미 (type: integer)

API 명세서나 JSON Schema에서

 
type: integer

라고 하면,
그 필드는 반드시 정수만 들어와야 한다는 뜻이에요.

예시:

properties:
  id:
    type: integer
  name:
    type: string

→ 이 말은

  • id는 숫자(정수)여야 하고,
  • name은 문자열이어야 한다는 뜻이에요.

🧠 비교: integer vs number

     
integer 정수만 허용 1, 0, -42
number 정수 + 소수 둘 다 허용 (실수) 1, 0.5, 3.14

예시:

 
age:
  type: integer  # 25 (✅), 25.5 (❌)

rating:
  type: number   # 4.5 (✅), 5 (✅)

⚙️ 실제 JSON 예시

유효한 데이터

 
{
  "id": 1,
  "price": 19.99
}

유효하지 않은 데이터 (스키마 위반)

 
{
  "id": "1",       // 문자열이라 오류
  "price": "19.99" // 문자열이라 오류
}

✅ 요약

용어 예시
integer 정수형 데이터 1, 0, -100
number 모든 숫자형 (소수 포함) 3.14, 42, -1.5
string 문자형 "hello", "123"
boolean 참/거짓 true, false

 


 

Q. schema - type: object ?

 

핵심은 👉 JSON은 데이터 "표현 형식"이고,
schema: type: object는 데이터의 "구조 정의" 라는 점이에요.

하나씩 정리해드릴게요.


💡 1️⃣ JSON은 “데이터 형식(포맷, format)”이에요

JSON(JavaScript Object Notation)은
데이터를 주고받을 때 쓰는 텍스트 형태의 표현 방식이에요.

 

예를 들어 아래는 JSON “데이터”죠:

 
{
  "name": "Alice",
  "age": 25
}

여기서 중요한 건:
이건 문자열로 된 데이터 표현일 뿐이고,
그 안에는 문자열, 숫자, 객체, 배열 같은 자료형(type) 들이 들어 있습니다.


💡 2️⃣ schema는 “데이터 구조(모양)”를 정의하는 설계도예요

OpenAPI에서 schema는
이 JSON어떤 구조로 되어 있어야 하는가?”를 기술한 설계서(blueprint) 역할을 합니다.

예를 들어 Swagger에서 이렇게 쓴다면 👇

 
requestBody:
  content:
    application/json:
      schema:
        type: object
        properties:
          name:
            type: string
          age:
            type: integer

이건 다음 의미예요:

“요청 본문application/json 타입의 데이터이고,
그 JSON객체(object) 형태이며,
그 안에는 name(문자열)age(정수)가 있어야 한다.”

 

즉, 실제 요청 데이터이렇게 생긴 JSON이 된다는 뜻이에요 👇

 
{
  "name": "Alice",
  "age": 25
}

💡 3️⃣ 왜 type: object라고 하냐면

type: 아래에 들어가는 건 JSON 내부 자료형이에요.

 

JSON은 다음 6가지 기본 자료형을 가지고 있습니다 👇

JSON 타입 예시 설명
object { "name": "Alice" } 키-값 쌍의 집합
array [1, 2, 3] 값들의 순서 있는 리스트
string "hello" 문자열
integer 5 정수
number 3.14 실수
boolean true 참/거짓

즉,
JSON 자체는 하나의 포맷(문자열 형태)이고,
안에 어떤 타입 구조(object/array 등) 를 가지는지를 schema에서 설명하는 거예요.


💡 4️⃣ 비유로 이해하면

개념 역할 비유
JSON 실제 데이터 파일 완성된 도시
schema 데이터 구조 정의서 도시의 설계도
type: object JSON의 구조(객체 형태) 도시 안에 건물들이 있는 형태

즉,

“이 API는 JSON 데이터를 주고받는다.”
그 JSON의 구조객체 형태 (type: object).”

 

두 문장은 전혀 모순되지 않습니다 —
서로 다른 관점에서 데이터를 설명하는 거예요.


💡 5️⃣ 실제로는 이렇게 세 겹으로 표현돼요

requestBody:
  content:
    application/json:      # JSON 포맷 (데이터 형태)
      schema:              # 구조 정의 (설계도)
        type: object       # JSON이 객체 형태임
        properties:
          title:
            type: string
          content:
            type: string

→ application/json : 데이터가 JSON으로 전달된다
→ schema : 그 JSON이 어떤 구조를 가져야 하는지 정의한다
→ type: object : JSON의 루트객체이다


정리

구분 의미 예시
JSON 데이터 포맷 (전송 형태) { "name": "Alice" }
schema 데이터 구조 정의 name은 string, age는 integer
type: object JSON이 “객체” 구조라는 뜻 { ... } 형태

 


🧩 2. 엔티티(테이블) 도출 및 관계 정의

도출된 리소스를 중심으로 다음을 설계합니다:

엔티티 설명 주요 컬럼 관계
users 사용자 정보 id, password, name, email posts(1:N)
posts 게시글 id, title, body, author_id users(N:1), comments(1:N)
comments 댓글 id, content, post_id, author_id posts(N:1), users(N:1)

👉 ERD(Entity Relationship Diagram) 도구로 시각화:

  • 추천: dbdiagram.io, Draw.io, ERDCloud, Lucidchart

 

Q. 엔티티 (Entity)

더보기

이제 데이터베이스 설계의 핵심 개념인 "엔티티(Entity)" 를 배우시려는 거네요.
이건 정말 중요합니다 — API, ORM, ERD, 모든 데이터 설계의 출발점이에요.

 

하나씩 단계적으로 정리해드릴게요 👇


💡 1️⃣ 엔티티(Entity)란?

엔티티(Entity) = 데이터베이스에 저장할 가치가 있는 “대상(thing)

 

즉, **데이터베이스에 저장해야 하는 실제 세계의 개체(객체)**를 말합니다.
사람, 상품, 주문, 게시글, 댓글 … 이런 것들이 전부 엔티티예요.


예시 1 — 블로그 시스템

실제 세상 DB에서의 엔티티 테이블 이름
사용자 User users
게시글 Article articles
댓글 Comment comments

즉, 엔티티는 테이블의 추상적 개념이라고 보면 됩니다.
DB에선 엔티티마다 하나의 테이블이 생겨요.


💡 2️⃣ 엔티티의 구성요소

엔티티는 보통 속성(attribute) 들을 가집니다.
→ 테이블로 보면 컬럼(column) 이죠.

 

예를 들어 User 엔티티를 보면:

 

속성 (컬럽) 설명 예시값
id 고유 식별자 (Primary Key) 1
name 이름 “Alice”
email 이메일 주소 alice@example.com
created_at 가입일 2025-10-09

이런 속성들이 모여서 하나의 엔티티(=테이블) 를 구성합니다.


💡 3️⃣ 엔티티 간 관계 (Relationship)

현실 세계의 대상들은 서로 관계가 있죠.
DB에서도 엔티티들은 1:1, 1:N, N:M 관계를 가집니다.

관계 예시 설명
1:1 사용자 ↔ 프로필 한 사용자당 -> 하나의 프로필
1:N 사용자 ↔ 게시글 한 사용자가 -> 여러 게시글 작성
N:M 게시글 ↔ 태그 한 게시글에 -> 여러 태그 
한 태그가 -> 여러 게시글에 연결

이 관계를 시각적으로 표현한 게 바로 ERD (Entity Relationship Diagram) 입니다.


💡 4️⃣ 엔티티 ≠ 객체, 하지만 비슷함

엔티티 DB 설계 용어이고,
객체(Object)코드(예: JavaScript, Python, Java)에서의 용어지만

 

둘은 개념적으로 거의 1:1로 대응합니다.

개념 DB 코드
엔티티(Entity) users 테이블 User 클래스
속성(Attribute) 컬럼(column) 클래스의 필드(field)
관계(Relationship) 외래키(Foreign Key) 참조(reference)

예:

 
// 코드 (TypeORM 예시)
@Entity()
class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;
}

이건 결국 DB의 users 엔티티(테이블)에 해당하는 구조예요.


💡 5️⃣ 실무에서 엔티티를 정리하는 방법

보통 이렇게 표로 정리합니다 👇

엔티티 속성 데이터타입 제약조건
User id INT PK
  name VARCHAR(50) NOT NULL
  email VARCHAR(100) UNIQUE
Article id INT PK
  title VARCHAR(255) NOT NULL
  author_id INT FK → User.id

✅ 요약

용어 의미 예시
엔티티(Entity) 저장할 수 있는 "대상" User, Product, Order
속성(Attribute) 엔티티의 특징 name, email, created_at
식별자(Key) 각 엔티티구별하는 값 id
관계(Relationship) 엔티티 간 연결 1:N, N:M

🧠 비유하자면

  • 엔티티 → “엑셀 시트
  • 속성(컬럼) → “엑셀의 (Column)”
  • 레코드(인스턴스) → “엑셀의 한 줄(Row)”

 


Q. TypeORM 이란?

 

TypeORM은 백엔드 개발에서 정말 자주 쓰이는 개념이에요.
하나씩 쉽게 풀어 설명해드릴게요.


💡 1️⃣ TypeORM이란?

TypeORM = TypeScript/JavaScript용 ORM(Object-Relational Mapping) 라이브러리

 

즉, 데이터베이스 테이블 ~ 객체(Object)를 연결해주는 도구입니다.


🔍 핵심 개념

  • ORM(Object-Relational Mapping)
    DB 테이블을 = **코드 안의 객체(클래스)**로 매핑(mapping)해서 사용
  • TypeORM은 TypeScript 친화적타입 안정성 제공
  • 다양한 DB 지원: MySQL, PostgreSQL, SQLite, SQL Server 등

💡 2️⃣ 왜 TypeORM을 쓰는가?

장점 설명
코드 중심 개발 SQL 없이 JS/TS 코드만으로 CRUD 가능
타입 안전성 TypeScript와 완전히 호환, 컴파일 시 타입 체크 가능
DB 독립성 MySQL ↔ PostgreSQL ↔ SQLite 쉽게 변경 가능
마이그레이션 지원 테이블 구조 변경 시 스크립트 생성 가능

💡 3️⃣ 간단 예시

엔티티 정의 

import { Entity, PrimaryGeneratedColumn, Column } from "typeorm";

@Entity()                 // 이 클래스가 DB 테이블임을 선언
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column({ unique: true })
  email: string;
}
  • @Entity() → 테이블 선언
  • @PrimaryGeneratedColumn() → 자동 증가 PK
  • @Column() → 테이블 컬럼 정의

CRUD 예시

import { getRepository } from "typeorm";

const userRepo = getRepository(User);

// Create
const newUser = userRepo.create({ name: "Alice", email: "alice@example.com" });
await userRepo.save(newUser);

// Read
const user = await userRepo.findOneBy({ id: 1 });

// Update
user.name = "Alice Smith";
await userRepo.save(user);

// Delete
await userRepo.remove(user);

✔ 여기서 한 가지 눈에 띄는 점:
SQL 문을 직접 쓰지 않아도 테이블과 컬럼이 매핑되어 CRUD가 가능하다는 점!


💡 4️⃣ 마이그레이션

TypeORM은 데이터베이스 구조 변경도 지원합니다.

 
# 새로운 컬럼 추가 시
typeorm migration:generate -n AddAgeToUser
typeorm migration:run
  • 기존 테이블 데이터 손실 없이 구조 변경 가능

💡 5️⃣ 요약

용어 의미
TypeORM TypeScript용 ORM 라이브러리
ORM 객체 DB를 연결해주는 도구
Entity 테이블과 1:1 매핑되는 클래스
Repository 엔티티 CRUD 담당 객체
Migration 테이블 구조 변경 기록 + 적용

 

 


 

Q. 외래키 (Foreign Key)

 

데이터베이스 설계에서 **외래키(Foreign Key, FK)**는 핵심 개념 중 하나입니다.
하나씩 쉽게 풀어드릴게요.


💡 1️⃣ 외래키(Foreign Key)란?

외래키(FK) = 다른 테이블기본키(PK)를 참조하는 컬럼

 

즉, 테이블 간의 연결 고리 역할을 하며,
데이터의 무결성(Integrity) 을 보장합니다.


🔍 비유

  • User 테이블 = 사람 명단
  • Post 테이블 = 게시글 목록
  • 게시글 작성자(Post.author_id) 정보를 Post에 저장할 때 [FK] → User.id를 참조 [PK]
  • 이 참조 컬럼이 바로 외래키(FK)
Post.author_id → User.id

💡 2️⃣ 예시 테이블

User 테이블

id (PK) name
1 Alice
2 Bob

Post 테이블

id (PK) title author_id (FK)
10 Hello World 1
11 My Post 2
  • author_id 컬럼User.id를 참조하는 외래키입니다.
  • 의미: 게시글 작성자반드시 존재하는 사용자여야 함.

💡 3️⃣ 외래키의 특징

  1. 참조 무결성 보장
    • FK로 연결된 값반드시 참조 테이블에 존재해야 함
    • 예: Post.author_id = 3User 테이블에 id=3이 없으면 오류
  2. 테이블 관계 표현
    • 1:N 관계 → FK가 N 쪽 테이블에 존재
    • 예: User(1)Post(N)
  3. ON DELETE / ON UPDATE 옵션
    • 참조 데이터가 삭제/수정될 때 FK 동작 정의 가능
FOREIGN KEY (author_id) REFERENCES users(id)
ON DELETE CASCADE  -- User 삭제 시 관련 Post도 삭제
ON UPDATE CASCADE  -- User PK 변경 시 Post.author_id도 자동 변경

💡 4️⃣ SQL 예시

CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name VARCHAR(50)
);

CREATE TABLE posts (
  id SERIAL PRIMARY KEY,
  title VARCHAR(100),
  author_id INT,
  FOREIGN KEY (author_id) REFERENCES users(id)
);
  • posts.author_id가 users.id를 참조
  • User 테이블에 없는 값들어갈 수 없음

💡 5️⃣ 요약

용어 의미 예시
Primary Key (PK) 테이블의 고유 식별자 users.id
Foreign Key (FK) 다른 테이블 PK 참조 posts.author_id → users.id
참조 무결성 FK 값은 반드시 참조 테이블에 존재 author_id=1 → User.id=1 존재

 


 

Q. 무결성 이란?

 

데이터베이스에서 말하는 무결성(Integrity) 은 아주 중요한 개념이에요.
쉽게 말하면 데이터 항상 올바르고 일관되게 유지되는 상태를 의미합니다.


💡 1️⃣ 무결성(Integrity)란?

무결성(Integrity) = 데이터의 정확성, 일관성, 신뢰성을 보장하는 성질

 

즉,

  • 잘못된 데이터가 들어가지 않도록 하고
  • 테이블 간 데이터가 서로 모순되지 않도록 관리하는 것

🔍 2️⃣ 무결성의 종류

① 참조 무결성(Referential Integrity)

  • **외래키(Foreign Key)**와 관련
  • 한 테이블의 값이 -> 다른 테이블에 반드시 존재해야 함
  • 예:
 
posts.author_id → 반드시 users.id에 존재해야 함

개체 무결성(Entity Integrity)

  • **기본키(PK)**와 관련
  • 테이블의 기본키NULL이 될 수 없고, 유일해야
  • 예:
users.id → 항상 고유, NULL 불가

도메인 무결성(Domain Integrity)

  • 컬럼 값의 타입 / 범위 / 형식을 제한
  • 예:
age INT CHECK(age >= 0)  -- 나이는 0 이상
email VARCHAR(100)       -- 최대 100자
  • 타입: INT 
  • 범위: CHECK(age >= 0)
  • 형식: VARCHAR(100) -- 최대 100자
 
 
사용자 정의 무결성(User-defined Integrity)
  • 특정 비즈니스 규칙을 적용
  • 예:
    • 주문 금액 ≥ 0
    • 회원 등급은 “bronze, silver, gold” 중 하나

💡 3️⃣ 비유로 이해하기

  • 무결성 없는 데이터 → 엉망진창 엑셀
  • 무결성 있는 데이터 → 항상 규칙일관성을 지키는 관리된 엑셀

예:

user_id post_title author_id
1 Hello 1
2 Hi 3

💡 4️⃣ 요약

용어 설명 예시
무결성(Integrity) 데이터의 정확성일관성 PK, FK / 컬럼 타입, 범위 제한
참조 무결성 FK가 참조 테이블 존재 posts.author_id → users.id
개체 무결성 PK는 유일, NULL 불가 users.id
도메인 무결성 컬럼 타입/범위 제한 age ≥ 0
사용자 정의 무결성 비즈니스 규칙 적용 주문 금액 ≥ 0

 

 

 

Q. SERIAL / VARCHAR()

 

SERIAL과 VARCHAR()는 DB 컬럼의 데이터 타입을 정의할 때 사용됩니다. 하나씩 자세히 설명할게요.


💡 1️⃣ SERIAL이란?

SERIAL = 자동 증가 정수형(Integer Auto Increment)

 

즉, 새로운 레코드가 들어올 때마다 DB가 알아서 1씩 증가시키는 정수 컬럼이에요.

🔍 특징

  • 주로 Primary Key로 사용
  • 자동으로 고유값 생성 → 따로 값을 넣어줄 필요 없음
  • PostgreSQL에서 지원하는 타입

📘 예시

CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name VARCHAR(50)
);
  • 첫 번째 레코드 → id = 1
  • 두 번째 레코드 → id = 2
  • 세 번째 레코드 → id = 3
  • 자동 증가

💬 MySQL에서는 SERIAL 대신 INT AUTO_INCREMENT를 사용합니다.

 


💡 2️⃣ VARCHAR(n)이란?

VARCHAR(n) = 가변 길이 문자열(Variable-length Character, 최대 n 글자)

 

즉, 최대 n글자까지 들어갈 수 있는 텍스트 컬럼입니다.

🔍 특징

  • 고정 길이 CHAR보다 공간 효율적
  • 문자열 길이에 따라 저장 공간이 달라짐

📘 예시

name VARCHAR(50)
  • name 컬럼최대 50글자까지 저장 가능
  • “Alice” → 5글자 → 필요한 만큼만 저장
  • “This is a long name” → 21글자 → 저장 가능

참고: VARCHAR()는 텍스트 길이를 제한할 수 있지만, 너무 작은 값으로 설정하면 긴 문자열 삽입 시 오류 발생


💡 3️⃣ 간단 비교

타입설명예시
타입 설명 예시
SERIAL 자동 증가 정수 1, 2, 3 … (주로 PK)
VARCHAR(n) 최대 n글자 가변 문자열 'Alice', 'Hello World'

💡 4️⃣ 예시 테이블

 
CREATE TABLE users (
  id SERIAL PRIMARY KEY,   -- 자동 증가 정수
  name VARCHAR(50),        -- 최대 50글자 문자열
  email VARCHAR(100) UNIQUE -- 최대 100글자, 유일
);
  • id → 레코드마다 자동 증가하는 고유값
  • name → 최대 50글자 이름 저장
  • email → 최대 100글자, 중복 불가

 

Q. id 도 중복되면 안 되잖아. 근데 UNIQUE 안 쓰고, SERIAL 만 쓰는 건, 어차피 1씩 증가되서 중복될 일이 없어서 안 쓰는 거야?

 

말씀하신 대로 SERIAL 자체만으로는 중복을 막을 수 없지만,

일반적으로 Primary Key(PK)로 사용하면 자동으로 UNIQUE가 적용되기 때문에 따로 UNIQUE를 붙이지 않아도 됩니다.

하나씩 자세히 설명할게요.


💡 1️⃣ SERIAL과 중복

  • SERIAL은 자동 증가 정수형이어서 레코드가 들어올 때마다 1씩 증가합니다.
  • 예를 들어:
id SERIAL​

 


레코드 id 값
1 1
2 2
3 3
  • 일반적으로 레코드가 삽입될 때 DB가 자동으로 증가값을 할당
  • 하지만, 그냥 SERIAL만 쓰면 UNIQUE/PK 제약이 없기 때문에 수동으로 같은 값 삽입 가능
    • 예: INSERT INTO users(id, name) VALUES (2, 'Alice') → 강제로 중복 가능

💡 2️⃣ PRIMARY KEY와 SERIAL

id SERIAL PRIMARY KEY
  • PRIMARY KEY를 붙이면 중복 방지 + NULL 불가가 자동 적용됩니다.
  • 즉, 자동 증가 + 유일성 + NOT NULL을 동시에 만족
  • 그래서 보통 SERIAL을 PK로 사용하면 UNIQUE를 따로 쓰지 않아도 됨

✅ 예시

CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name VARCHAR(50)
);
  • 첫 번째 레코드 → id=1
  • 두 번째 레코드 → id=2
  • 중복 삽입 시 DB가 오류 발생 (PK 위반)

💡 3️⃣ 결론

컬럼 정의 중복 가능? NULL 가능? 설명
id SERIAL ✅ 가능 (강제로 넣으면 중복 가능) 가능 그냥 숫자 증가만
id SERIAL PRIMARY KEY ❌ 불가 ❌ 불가 자동 증가 + 유일 + NOT NULL → 보통 이렇게 씀
id SERIAL UNIQUE ❌ 불가 가능 자동 증가 + 유일, PK 아님

즉, 중복을 막으려면 PK 또는 UNIQUE를 명시해야 함
그냥 SERIAL만 쓰면 “자동 증가”라는 장점만 있고, 유일성 보장은 제약조건이 없으면 깨질 수 있음

 


Q. NULL / NOT NULL

 

데이터베이스에서 NULL과 NOT NULL 개념은 아주 기본적이면서도 핵심이에요.
하나씩 차근차근 설명할게요.


💡 1️⃣ NULL이란?

NULL = 값이 없음, 비어 있음, 정의되지 않음

 

즉, **“아직 데이터가 존재하지 않는다”**라는 의미예요.

🔍 예시

 
CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name VARCHAR(50) NOT NULL,
  email VARCHAR(100)
);
  • 여기서 email 컬럼아무 값도 안 넣으면NULL(값이 없음)
  • 의미: Alice의 이메일 정보가 아직 없음

💡 2️⃣ NOT NULL이란?

NOT NULL = 값이 반드시 존재해야

 

즉, 이 컬럼에는 빈 값(NULL)을 넣을 수 없음이라는 제약입니다.

🔍 예시

 
CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name VARCHAR(50) NOT NULL,
  email VARCHAR(100)
);
  • name 컬럼은 무조건 값이 있어야 함 → 없으면 오류
  • email 컬럼은 선택적값 없으면 NULL 가능
INSERT INTO users (name) VALUES ('Alice');   -- OK
INSERT INTO users (name, email) VALUES (NULL, 'alice@example.com'); -- ❌ 오류

💡 3️⃣ NULL과 NOT NULL 비교

컬럼 설명 가능 값
name VARCHAR(50) 값 없어도 됨 (선택) 'Alice', NULL
name VARCHAR(50) NOT NULL 값 반드시 존재 (필수) 'Alice', '' (빈 문자열은 가능), NULL

💬 주의: NULL ≠ 빈 문자열('')

NULL → 값이 없음

'' → 값은 존재하지만 길이가 0인 문자열


💡 4️⃣ PK, UNIQUE와 NOT NULL 관계

  • Primary Key → 자동으로 NOT NULL + UNIQUE
  • Unique 컬럼 → 기본적으로 NULL 허용 가능
    • 하지만 데이터 무결성을 위해 보통 NOT NULL 같이 사용

💡 5️⃣ 요약

용어 의미 예시
NULL 값이 없음 email = NULL
NOT NULL 값이 반드시 존재해야 name NOT NULL
빈 문자열('') 값은 있지만, 길이가 0 (문자열) name = ''
PK 자동 NOT NULL + UNIQUE id SERIAL PRIMARY KEY

⚙️ 3. 정규화(및 비정규화 고려)

  1. 1NF: 컬럼은 원자값만 (리스트 X)
  2. 2NF: 부분 종속 제거
  3. 3NF: 이행 종속 제거 (데이터 중복 최소화)
  4. 비정규화: 조회 성능 필요 시 일부 중복 허용 (ㅠ칼럼 등)

예:

  • 댓글 수 카운트를 posts.comment_count로 미리 저장해두는 등

🧠 4. 실제 스키마 설계 (SQL 수준)

예를 들어 PostgreSQL 기준:

CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name VARCHAR(50) NOT NULL,
  email VARCHAR(100) UNIQUE NOT NULL,
  password VARCHAR(255) NOT NULL,
  created_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE posts (
  id SERIAL PRIMARY KEY,
  title VARCHAR(200) NOT NULL,
  body TEXT,
  author_id INT REFERENCES users(id),
  created_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE comments (
  id SERIAL PRIMARY KEY,
  content TEXT NOT NULL,
  author_id INT REFERENCES users(id),
  post_id INT REFERENCES posts(id),
  created_at TIMESTAMP DEFAULT NOW()
);

 

Q. TIMESTAMP / DEFAULT / NOW()

더보기

아주 흔하게 쓰이는 DB 컬럼 정의죠.
created_at TIMESTAMP DEFAULT NOW()를 하나씩 풀어서 설명할게요.


💡 1️⃣ created_at

  • 단순히 **컬럼 이름(column name)**입니다.
  • 일반적으로 레코드가 생성된 시각을 저장하는 용도로 사용합니다.
  • 이름은 자유롭게 바꿀 수 있지만, 관례적으로 created_at, updated_at 같은 이름을 씁니다.

💡 2️⃣ TIMESTAMP (PostgreSQL) / DATETIME (MySQL)

TIMESTAMP = 날짜시간 정보를 저장하는 데이터 타입

  • 시각과 날짜를 함께 저장합니다.
  • 예: '2025-10-09 14:32:00'
  • PostgreSQL 기준으로 1970년 1월 1일부터의 시간(UTC)까지 관리할 수 있음
  • TIMESTAMP 대신 DATETIME을 쓰는 DB도 있음 (MySQL 등)

💡 3️⃣ DEFAULT NOW()

DEFAULT NOW() = 값을 지정하지 않으면 현재 시각을 자동으로 넣어라

  • 레코드 생성 시 값이 생략되면 DB가 자동으로 현재 시간을 넣어줌
  • 예:
INSERT INTO users (name) VALUES ('Alice');
  • 만약 created_at 컬럼에 값 지정 안하면자동으로 2025-10-09 14:32:00 같은 현재 시각이 들어감

💡 4️⃣ 예시 전체

CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name VARCHAR(50) NOT NULL,
  created_at TIMESTAMP DEFAULT NOW()
);
칼럼 의미
id 자동 증가 PK
name 사용자 이름, NOT NULL
created_at 레코드 생성 시각, 기본값 = 현재 시각

삽입 예시

INSERT INTO users (name) VALUES ('Alice');
  • 결과 테이블:
id name created_at
1 Alice 2025-10-09 14:32:00

✔ 수동으로 값을 넣어도 됨:

 
INSERT INTO users (name, created_at) VALUES ('Bob', '2025-01-01 10:00:00');

💡 5️⃣ 요약

용어 의미 예시
created_at 컬럼 이름, 생성 시각 저장 '2025-10-09 14:32:00'
TIMESTAMP 날짜+시간 저장 타입 '2025-10-09 14:32:00'
DEFAULT NOW() 값 없으면 자동으로 현재 시간 삽입 INSERT 시 생략하면 현재 시각 자동

 


 

Q. DEFAULT랑 NOW() 는 항상 같이 쓰는거야? TIMESTAMP 만 있고, DEFAULT NOW() 안 쓰면 어떻게 나와?

 

**답은 “아니요, 항상 세트로 다니는 건 아니다”**입니다.


1️⃣ DEFAULT와 NOW()의 관계

  • DEFAULT
    • 컬럼에 값이 없을 때 무슨 값을 기본으로 넣을지 지정하는 키워드
    • 예: DEFAULT 0, DEFAULT 'unknown'
    • 값만 지정해주면 어떤 것이든 가능
  • NOW()
    • 현재 시각을 반환하는 함수(Function)
    • TIMESTAMP 타입과 주로 함께 쓰지만, 다른 곳에서도 함수로 사용 가능

🔹 함께 쓰는 경우

created_at TIMESTAMP DEFAULT NOW()
  • 의미: 값 생략 시 현재 시각 넣어라
  • TIMESTAMP 컬럼에서 자주 쓰임

🔹 따로 쓰는 경우

 
price INT DEFAULT 0
  • 숫자 컬럼에 기본값 0 넣기
  • 이때 NOW()와는 전혀 상관 없음
 
created_at TIMESTAMP
  • TIMESTAMP만 있고 DEFAULT 안 붙이면값 없으면 NULL
  • 이때 NOW()도 붙이지 않음 → 자동 시간 입력 없음

✅ 요약

키워드 의미 항상 같이 쓰나?
DEFAULT 값이 없으면 기본값 넣기 X (값이면 어떤 것이든 가능)
NOW() 현재 시각 반환 X (DEFAULT나 INSERT에만 활용)
DEFAULT NOW() 생략 시 현재 시각 자동 입력 O (TIMESTAMP 컬럼에서 자주 사용)

즉, DEFAULT와 NOW()는 서로 독립적이며, TIMESTAMP 컬럼에서 현재 시간을 자동 입력하고 싶을 때만 같이 씁니다.

 

 

이제 “created_at TIMESTAMP만 있을 때” 동작을 정확히 정리해드릴게요.


💡 1️⃣ 컬럼 정의

 
CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name VARCHAR(50),
  created_at TIMESTAMP
);
  • created_at 컬럼 타입은 TIMESTAMP
  • DEFAULT나 NOT NULL 제약 없음 → 값 생략 가능

💡 2️⃣ 값 생략 시

INSERT INTO users (name) VALUES ('Alice');
  • created_at 값을 넣지 않았으므로NULL이 들어갑니다.

결과 테이블:

id name created_at
1 Alice NULL

 


💡 3️⃣ 값 지정 시

 
INSERT INTO users (name, created_at) VALUES ('Bob', '2025-01-01 10:00:00');
  • 지정한 값 그대로 들어갑니다.

결과:

id name created_at
2 Bob 2025-01-01 10:00:00

💡 4️⃣ 요약

정의 값 생략 시 특징
TIMESTAMP NULL 값 없으면 NULL, 자동 입력 없음
TIMESTAMP DEFAULT NOW() 현재 시각 값 생략 시 자동으로 현재 시간 입력

💬 핵심: TIMESTAMP만 있으면 “자동 시간 입력”이 되지 않고, 값 생략 시 NULL이 들어감
자동으로 생성 시각을 기록하고 싶다면 DEFAULT NOW()를 써야 합니다.

 


Q. NOW() 는 현재 시각을 반환하는 '함수'라며. 이 함수는 어디에 정의되어있어서, 우리가 호출해서 쓸 수 있는 거야? MySQL 이나 PostgreSQL 에?

 

맞습니다, **NOW()는 DBMS가 제공하는 “내장 함수(built-in function)”**입니다.


1️⃣ 어디에 정의되어 있나?

  • NOW() 함수MySQL, PostgreSQL DBMS 내부에 정의되어 있음
  • 즉, 우리가 직접 구현한 함수가 아니라 DB가 기본으로 제공하는 것

2️⃣ DB에서 어떻게 동작하나?

  • NOW()를 호출하면 DB가 현재 서버 시각을 반환합니다.
  • 예를 들어:

PostgreSQL

SELECT NOW();

결과:

2025-10-09 15:10:32.123456+00

MySQL

SELECT NOW();

결과:

 
2025-10-09 15:10:32

💬 DB 종류에 따라 출력 형식이나 정밀도(소수점 이하 초)가 조금 다를 수 있음


3️⃣ 어디서 사용할 수 있나?

  1. INSERT 시 기본값
INSERT INTO users (name, created_at) VALUES ('Alice', NOW());
  1. 컬럼 DEFAULT 값
created_at TIMESTAMP DEFAULT NOW()
  1. SELECT, UPDATE 등 쿼리 내에서 계산
UPDATE users SET last_login = NOW() WHERE id = 1;

4️⃣ 특징

  • DB 서버 시각 기준클라이언트 시각과 다를 수 있음
  • 함수이므로 괄호() 사용 → NOW()
    • PostgreSQL에서는 CURRENT_TIMESTAMP와 동의어
    • MySQL도 동일하게 지원

✅ 요약

항목 의미
NOW() DB가 제공하는 내장 함수, 현재 시각 반환
정의 위치 MySQL, PostgreSQL 등 DBMS 내부
사용 가능 위치 SELECT ~ FROM ,
INSERT INTO ~ VALUES,
UPDATE ~ SET,
DEFAULT

등 쿼리 내

 


 

Q. DBMS

 

DBMS는 데이터베이스를 다루는 핵심 시스템이에요.


💡 1️⃣ DBMS란?

DBMS(Database Management System) = 데이터베이스 관리 시스템

즉, 데이터를 안전하고 효율적으로회, 수정, 저장, 관리할 수 있도록 도와주는 소프트웨어입니다.


2️⃣ DBMS가 하는 일

  1. 데이터 저장
    • 데이터를 구조화하여 디스크에 안전하게 저장
    • 예: 테이블, 컬럼, 레코드 형태
  2. 데이터 조회/수정
    • SQL 같은 질의 언어로 데이터를 읽고 쓰는 기능 제공
  3. 무결성 유지
    • PK, FK / NOT NULL, UNIQUE 등의 제약 조건을 적용
  4. 동시성 제어
    • 여러 사용자가 동시에 접근해도 데이터 일관성 유지
  5. 보안 관리
    • 권한접근 제어

3️⃣ DBMS 종류

DBMS 특징
MySQL 오픈소스 / 웹 서비스에서 많이 사용
PostgreSQL 오픈소스, 기능 강력, 복잡한 쿼리 가능
SQLite 가벼움, 파일 기반 / 모바일/임베디드
Oracle DB 상용, 대규모 기업용
SQL Server Microsoft 상용, Windows 환경 최적화

4️⃣ DBMS vs 데이터베이스

  • 데이터베이스(Database)
    • 실제 데이터를 저장하는 공간
    • 예: users 테이블, articles 테이블
  • DBMS
    • 데이터를 관리하는 소프트웨어
    • 예: MySQL, PostgreSQL, Oracle

즉, DBMS가 있어야 데이터베이스를 만들고 관리할 수 있음


✅ 요약

용어 의미 예시
DBMS 데이터 관리 시스템 MySQL, PostgreSQL
Database 데이터를 저장하는 공간 my_blog_db
Table 데이터를 구조화 단위 users, posts
Row 레코드 id=1, name='Alice'
Column 속성 name, email

 


 

Q. 레코드 

데이터베이스에서 **레코드(Record)**는 핵심 단위 중 하나입니다.


💡 1️⃣ 레코드(Record)란?

레코드(Record) = 테이블 한 줄(Row)에 들어있는 데이터 단위

 

즉, 하나의 개체(Entity) 정보를 담고 있는 데이터 집합입니다.

  • 테이블 = 사람들을 저장하는 구조라면
  • 레코드 = 한 사람 정보 한 줄

🔍 예시 테이블

users 테이블:

id name email
1 Alice alice@example.com
2 Bob bob@example.com
  • 첫 번째 레코드: id=1, name='Alice', email='alice@example.com'
  • 두 번째 레코드: id=2, name='Bob', email='bob@example.com'

🔹 한 줄 전체가 레코드입니다.
🔹 각각의 컬럼(Column) 값이 모여 하나의 레코드가 됨


💡 2️⃣ 레코드 vs 컬럼

용어 의미 예시
컬럼(Column) 테이블의 속성 id, name, email
레코드(Record) 한 행(Row), 속성값들의 집합 1, Alice, alice@example.com
  • 컬럼은 가로 축 (속성)
  • 레코드는 세로 축 (데이터 한 단위)

💡 3️⃣ ERD 관점

  • 엔티티(Entity) = 테이블 설계
  • 속성(Attribute) = 컬럼
  • 레코드(Record) = 엔티티 인스턴스(실제 데이터)

예: User 엔티티 → 레코드 1개 = Alice라는 실제 사용자 데이터


✅ 요약

용어 DB 관점 의미 코드/비유
테이블 데이터 구조
(users / post ..)
클래스
컬럼 속성
(id, name, email ..)
클래스 필드
레코드 한 데이터 단위
(id=1 / name='Alice' / email="abc@naver.com")
클래스 인스턴스/객체
행(Row) 레코드와 동일 한 줄 데이터

 


 

Q. 클래스 필드 / 클래스 인스턴스 / 클래스 객체

이제 데이터베이스와 객체 지향 개념을 연결해서 이해해보면 좋습니다.
클래스, 필드, 인스턴스, 객체 개념을 하나씩 차근차근 설명할게요.


💡 1️⃣ 클래스(Class)란?

클래스 = 객체를 만들기 위한 설계도(템플릿)

  • 속성동작을 정의해 놓은 구조
  • 실제 데이터를 담고 있지 않고, “이런 구조를 가진 객체를 만들겠다”라는 설계

예시 (TypeScript)

 
class User {
  name: string;
  email: string;

  constructor(name: string, email: string) {
    this.name = name;
    this.email = email;
  }

  greet() {
    console.log(`Hello, ${this.name}`);
  }
}
  • User 클래스 = 설계도
  • 속성(name, email)과 동작(greet)을 정의

💡 2️⃣ 클래스 필드(Class Field)란?

클래스 필드 = 클래스 안에서 정의된 속성(attribute, property)

  • 객체가 가지는 데이터 항목
  • 위 예제에서 name과 email클래스 필드

💡 3️⃣ 클래스 인스턴스(Class Instance) / 객체(Object)

인스턴스 = 클래스로부터 실제로 생성구체적 데이터
객체 = 인스턴스와 동일하게 생각할 수 있음

 
const alice = new User('Alice', 'alice@example.com');
const bob = new User('Bob', 'bob@example.com');
  • alice = User 클래스의 인스턴스(객체)
  • bob = 또 다른 인스턴스(객체)

즉, 클래스는 설계도, 인스턴스/객체는 실제 데이터


💡 4️⃣ DB 테이블과 클래스 비교

DB 용어 객체 지향(OOP) 용어 설명
테이블 클래스 구조 설계
컬럼 클래스 필드 속성 정의
레코드 인스턴스/객체 실제 데이터 한 단위
행(Row) 인스턴스/객체 동일

예시 매칭


💡 5️⃣ 그림으로 비유

 
[User 클래스] ← 설계도
   name
   email
   greet()

      ↓ new User(...)

[alice 객체/인스턴스]
   name = 'Alice'
   email = 'alice@example.com'

[bob 객체/인스턴스]
   name = 'Bob'
   email = 'bob@example.com'

클래스 = 설계도
객체/인스턴스 = 설계도로 만든 실제 제품

 


 

TypeScript 클래스 구조를 완전히 이해하면 OOP와 DB 연관 개념도 훨씬 쉽게 이해돼요.


1️⃣ 클래스 전체 구조 설명

class User {
   name: string;
   email: string;

   constructor(name: string, email: string) {
      this.name = name;
      this.email = email;
  }

 greet() {
    console.log(`Hello, ${this.name}`);
 }
}
  • class User { ... } → User라는 클래스 정의
  • name: string; email: string; → 클래스 필드(속성), User 객체가 갖는 데이터
  • constructor(name: string, email: string) { ... } → 객체 생성 시 실행되는 초기화 함수
  • greet() → User 객체가 가진 동작(메서드), 콘솔에 인사 출력

즉, User 클래스는 설계도이고, new User('Alice', 'alice@example.com')로 **인스턴스(객체)**를 만들 수 있어요.


2️⃣ constructor와 메서드(greet)의 차이

  • constructor()
    • 클래스를 기반으로 객체를 만들 때 자동으로 호출되는 함수
    • 객체의 초기값을 설정하는 용도
    • 호출 방식: new User('Alice', 'alice@example.com')
  • greet()
    • 객체가 가진 일반 동작(메서드)
    • 호출 방식: user.greet()

💡 핵심:

  • constructor는 생성할 때 자동 실행되는 초기화 함수”
  • greet()는 객체 만든 후 호출하는 동작

3️⃣ constructor 매개변수 타입 지정 이유

constructor(name: string, email: string) { ... }
  • 클래스 필드 name: string;과 동일 타입을 지정하는 이유:
    1. 매개변수 타입 지정TS 컴파일러가 타입 체크 가능
    2. 객체 생성 시 ㅠ(new User(...))이 올바른 타입인지 확인
  • 즉, 필드 타입매개변수 타입이 같아야 필드에 안전하게 할당 가능

4️⃣ this.name = name; this.email = email; 설명

  • this → 현재 객체 자신을 가리킴
  • this.name = name 의미:
    • this.nameUser 객체의 필드 name
    • nameconstructor 매개변수 name
    • 즉, 객체 필드 매개변수 값을 할당
const alice = new User('Alice', 'alice@example.com');


// User 객체(인스턴스)
{
   name: 'Alice'
   email: 'alice@example.com'
}

5️⃣ ${this.name}와 문자열 보간

console.log(`Hello, ${this.name}`);
  • 백틱(`) + ${}템플릿 문자열(Template String)
  • ${} 안변수/표현식을 넣으면 문자열 안에 값을 넣을 수 있음

왜 this.name을 넣는가?

  • name → constructor 매개변수
  • this.name → 객체의 필드(name)
  • greet()는 객체가 만들어진 후 호출되므로,
    • 매개변수 name은 존재하지 않음 → constructor 밖에서는 this.name 사용
class User {
   name: string;
   email: string;

   constructor(name: string, email: string) {
      this.name = name;
      this.email = email;
  }

 greet() {
    console.log(`Hello, ${this.name}`);
 }
}

// 객체 생성할 때 -> 초기화함수 constructor() 실행
const alice = new User('Alice', 'alice@example.com');

// 객체 필드에 매개변수 값 할당
{
  name: 'Alice',
  email: 'alice@example.com'
}

alice.greet(); // Hello, Alice
  • greet()는 constructor 범위 밖 → 매개변수 name 접근 불가
  • 반드시 **this.name(객체 필드)**을 사용해야 함

✅ 정리

용어 의미
class User { ... } 설계도, 클래스 정의
name: string 클래스 필드, 객체 속성
constructor(name: string, email: string) 객체 생성 시 실행, 초기값 설정
this 현재 객체 자신
this.name = name 객체 필드(name)에 매개변수 값(name) 할당
greet() 객체가 가진 동작, 메서드
${this.name} 템플릿 문자열로 객체 필드 값을 -> 문자열 안에 삽입

 


 

 

Q. console.log(`Hello, ${this.name}`)

 

문자열 안에 동적으로 변하는 값을 넣을 때 **백틱(`) + ${}**를 사용합니다.
이걸 템플릿 문자열(Template String) 또는 템플릿 리터럴이라고 해요.


1️⃣ 사용 예시

const name = "Alice";
console.log(`Hello, ${name}!`);  // Hello, Alice!
  • ${name} → 변수 name의 값을 문자열 안에 넣음
  • 기존 "Hello, " + name + "!"처럼 + 연산자 없이 간단하게 작성 가능

2️⃣ 키보드에서 백틱(`) 입력 방법

  • Windows:
    • 보통 왼쪽 상단 숫자 1 왼쪽
    • Shift 없이 한 번 누르면 ` 입력
  • Mac:
    • 왼쪽 상단 숫자 1 왼쪽 키, Shift 없이
  • 주의:
    • 작은 따옴표 ' '나 큰 따옴표 " "와 다릅니다.
    • 백틱은 문자열 시작과 끝을 `로 감싸야 ${} 사용 가능

3️⃣ 기존 문자열과 비교

 
const name = "Alice";
const day = "Friday";

// 기존 방식
console.log("Hello, " + name + "!");

// 템플릿 문자열
console.log(`Hello, ${name}!`);

console.log(`Hello, ${name}!
Today is ${day}.`);
  • 템플릿 문자열이 훨씬 직관적이고, 줄바꿈, 변수 삽입도 쉽게 가능
 

결과:

 
Hello, Alice!
Today is Friday.

🚦 5. API ↔ DB 매핑 검증

이제 API 요청/응답 -> DB 구조에 맞게 동작하는지를 확인합니다.

예:

  • GET /users/{id}SELECT * FROM users WHERE id = $1
  • POST /posts → INSERT INTO posts (...) VALUES (...)
  • 응답 JSON 구조DB 스키마와 일관성 있는지 확인

 

Q. DBMS 쿼리

더보기

1️⃣ DBMS 쿼리 종류

SQL(Structured Query Language)은 크게 5가지 유형으로 나뉩니다.

분류 설명 예시
DDL (Data Definition Language) 데이터베이스 구조 정의 CREATE TABLE
ALTER TABLE
DROP TABLE
DML (Data Manipulation Language) 데이터 조작 (삽입/수정/삭제) INSERT
UPDATE
DELETE
DQL (Data Query Language) 데이터 조회 SELECT
DCL (Data Control Language) 권한 관리 GRANT
REVOKE
TCL (Transaction Control Language) 트랜잭션 관리 COMMIT
ROLLBACK
SAVEPOINT

💡 핵심:

  • DDL → 테이블 구조 변경
  • DML → 데이터 내용 변경
  • DQL → 데이터 조회

2️⃣ SELECT 쿼리

SELECT * FROM users WHERE id = $1;

🔹 구성 요소

구문 의미
SELECT * 모든 컬럼을 조회 (* = 전체 컬럼)
FROM users 조회할 테이블 이름
WHERE id = $1 조건 지정 / id가 $1인 행만 선택

🔹 $1 의미

  • $1 → 파라미터 바인딩(Placeholder)
  • 쿼리 실행 시 외부에서 값을 넣는 자리 표시자
  • 예: Node.js PostgreSQL 라이브러리(pg)에서
const id = 5;
const result = await pool.query('SELECT * FROM users WHERE id = $1', [id]);
  • $1 → 배열 [id]첫 번째 값(5)로 치환
  • SQL 인젝션 방지에도 중요

🔹 실행 과정 예시

users 테이블
id
----
1
2
  • id = 2를 바인딩하면 결과: (WHERE id = $2) (id가 $2인 행만 선택)
id name email
2 Bob bob@example.com

3️⃣ 요약

  • DBMS 쿼리 종류
    • DDL: 테이블 구조 변경
    • DML: 데이터 삽입/수정/삭제
    • DQL: 데이터 조회
    • DCL: 권한 관리
    • TCL: 트랜잭션 관리
  • SELECT * FROM users WHERE id = $1
    • users 테이블에서 특정 조건(id=$1)에 맞는 행 조회
    • $1은 파라미터 자리 표시자
    • Node.js 등에서 실제 값으로 치환 가능

 

1️⃣ SQL 인젝션(SQL Injection)

SQL 인젝션 =
악의적인 사용자가 입력값을 조작해서, DB 쿼리를 변조하고
의도치 않은 데이터조회/삭제/변경하는 공격

🔍 예시

const userInput = "' OR 1=1; --";
const query = `SELECT * FROM users WHERE name = '${userInput}'`;
  • 원래 의도: 특정 이름 조회 (users 테이블의 모든 컬럼 중, name이 ${userInput}인 행 조회)
  • 공격: ' OR 1=1; -- → 모든 데이터 조회 가능
  • 결과: DB에 있는 모든 사용자 정보를 가져올 수 있음

✅ 방어 방법

  • 파라미터 바인딩 사용 ($1, ?) → 값만 전달, 쿼리 구조는 안전
 
const result = await pool.query(
  'SELECT * FROM users WHERE name = $1',
  [userInput]
);
  • 이렇게 하면 입력값이 쿼리에 직접 삽입되지 않아 안전

2️⃣ CRUD

CRUD = 데이터베이스에서 기본적인 데이터 조작 작업 4가지

 

Create 생성 새로운 데이터 삽입 INSERT INTO users (name) VALUES ('Alice');  
Read 조회 데이터 조회 SELECT * FROM users WHERE id=1;  
Update 수정 데이터 수정 UPDATE users SET name='Bob' WHERE id=1;  
Delete 삭제 데이터 삭제 DELETE FROM users WHERE id=1;  
  • CRUD는 SQL 명령어와 1:1 매칭되는 개념
  • 즉, SQL(언어)로 -> CRUD 작업을 수행(행위)한다고 이해하면 됨

3️⃣ SQL과 CRUD의 관계

  • SQL(Structured Query Language) = 데이터베이스와 소통하기 위한 언어
  • CRUD = SQL로 수행하는 -> 기본 데이터 조작 작업 4가지 (생성,조회,수정,삭제)
  • 예: SQL은 언어, CRUD는 행위
SQL → 언어
CRUD → SQL로 할 수 있는 행동

🔹 요약

용어 의미
SQL Injection 사용자 입력값으로 쿼리를 조작하여 공격하는 기법
CRUD 데이터 생성, 조회, 수정, 삭제의 4가지 기본 동작
SQL 데이터베이스와 소통하기 위한 언어, CRUD 작업을 SQL로 수행

 


 

Q. 쿼리 (Query) 

한줄 정리

  • 쿼리(query) = 데이터베이스에 “요청(질문)”을 하는 한 건의 명령문(문장)
  • SQL = 그 쿼리를 작성하는 언어(도구) 중 하나 — Structured Query Language.
    즉, 쿼리 = (SQL로 쓸 수 있는) 하나의 요청문, SQL = 그 요청문들을 쓰는 언어라고 보면 됩니다.

좀 더 풀어서

1) 쿼리(query)는 무엇인가?

  • 쿼리는 정보를 얻거나(조회), 데이터를 추가/수정/삭제하는(조작) 모든 “데이터 관련 요청”을 의미합니다.
  • 보통 하나의 문장(statement) 로 표현됩니다. 예:
    • SELECT * FROM users WHERE id = 1; ← 조회 쿼리
    • INSERT INTO users (name) VALUES ('Alice'); ← 생성(조작) 쿼리
    • UPDATE users SET name = 'Bob' WHERE id = 1; ← 수정 쿼리

참고: 많은 사람은 “쿼리” 하면 SELECT(조회)를 먼저 떠올리지만, 엄격히 말하면 INSERT/UPDATE/DELETE 같은 것도 모두 쿼리입니다.


2) SQL과의 관계 :: SQL -> Query

  • SQL = 데이터베이스와 대화하기 위해 만든 표준 언어(문법)
  • SQL로 작성된 각 문장(Statement) 이 바로 쿼리입니다.
    • 즉, “SQL 쿼리”라고 부르면 그건 SQL 문장 하나(쿼리)를 말하는 것.
  • 비유:
    • SQL = 영어,
    • 쿼리 = 영어로 쓴 편지 한 통.

3) 쿼리의 종류(실무 관점)

  • 조회 쿼리 (SELECT) — 데이터 읽기 (주로 DQL)
  • 삽입 쿼리 (INSERT) — 새 레코드 추가 (DML)
  • 수정 쿼리 (UPDATE) — 기존 레코드 수정 (DML)
  • 삭제 쿼리 (DELETE) — 레코드 삭제 (DML)
  • 구조 변경 쿼리 (CREATE/ALTER/DROP)테이블/스키마 변경 (DDL)
  • 권한/트랜잭션 관련 쿼리 (GRANT, COMMIT)

모두 쿼리라고 부를 수 있습니다.


4) 쿼리 vs 명령(command) vs 질의(question)

  • 실무에서는 혼용해서 쓰지만 미세 차이:
    • 쿼리(query) / 질의(question): 주로 “데이터를 조회하는 요청” 의미로 쓰이기도 함.
    • 명령(command) / statement: 더 넓은 의미로 “DB에 실행을 지시하는 모든 문장” (조회·삽입·수정·삭제 포함).
  • 요약: 모두 같은 맥락에서 섞어 써도 무방하나, 엄밀히는 “쿼리 = DB에 대한 한 건의 요청문”.

5) 쿼리 실행의 실제 (짧은 흐름)

  1. 애플리케이션이 SQL 쿼리를 -> DB 드라이버에 보냄.
  2. 드라이버/DB쿼리 파싱 실행 계획 생성 → 실행결과 반환.
  3. (중요) 파라미터 바인딩을 사용해, 쿼리와 데이터를 분리하면 SQL 인젝션을 방어할 수 있음.

예:

 
-- 안전한 파라미터화 예 (Postgres)
SELECT * FROM users WHERE id = $1;
-- $1 자리에 실제 값이 바인딩되어 전달됨

6) 쿼리는 SQL만 있는 게 아니다

  • GraphQL 쿼리, Elasticsearch 쿼리 DSL, MongoDB 쿼리 객체
  • 즉, 쿼리라는 개념은 “데이터에 질문하는 표현” 자체이고, SQL은 관계형 DB에서의 표준 언어일 뿐입니다.

핵심 요약 (짧고 굵게)

  • 쿼리 = 데이터베이스에 하는 하나의 요청(문장)
  • SQL = 쿼리를 쓰는 언어(도구)
  • SELECT/INSERT/UPDATE/DELETE 등 모두 쿼리로 불릴 수 있다.
  • 쿼리 작성 시 **파라미터화(Prepared Statement)**로 보안(예: SQL 인젝션)과 성능을 챙기세요.

🧰 6. ORM(Model) 설계

ORM (ex: Prisma, TypeORM, Sequelize, SQLAlchemy) 을 사용한다면:

  • 모델 이름은 엔티티명과 일치
  • 관계는 명시적으로 정의 (hasMany, belongsTo 등)
  • 마이그레이션 스크립트 자동 생성

예 (TypeORM):

@Entity()
export class Post {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  title: string;

  @ManyToOne(() => User, user => user.posts)
  author: User;
}

📋 7. 검증 & 피드백

  • API 스펙 변경에 따른 스키마 영향 점검
  • 실제 테스트 데이터쿼리 성능 테스트
  • 비즈니스 로직에서 필요한 인덱스 추가

 

Q. 인덱스 란?

더보기

데이터베이스에서 “인덱스(Index)”는 성능과 직결되기 때문에 꼭 이해해야 합니다.


💡 1️⃣ 인덱스(Index)란?

인덱스(Index) = 책의 목차처럼 DB 검색 속도를 빠르게 해주는 구조

 

즉, DB에서 특정 데이터를 빠르게 찾기 위해 별도로 만든 자료 구조입니다.

  • 인덱스 없으면 DB는 테이블의 모든 데이터를 하나씩 뒤져야 해서 느려짐
  • 인덱스 있으면 바로 위치를 찾아서 조회 속도가 빨라짐

🔍 비유로 이해하기

  • = DB 테이블
  • 목차 = 인덱스
  • ‘Chapter 5’ 페이지가 어디인지 찾아라”
    • 목차 있음 → 바로 페이지 이동 ✅
    • 목차 없음 → 1페이지부터 하나씩 확인 ❌ (느림)

💡 2️⃣ 실제 예시

테이블

 
CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name VARCHAR(50),
  email VARCHAR(100)
);
  • id 컬럼은 기본키(primary key) 이므로 자동으로 인덱스가 생성됨 → 검색 빠름
  • 하지만 email 컬럼으로 자주 검색한다면 → 인덱스를 추가하는 게 좋음
 
CREATE INDEX idx_users_email ON users(email);

이제:

 
SELECT * FROM users WHERE email = 'alice@example.com';
  • 인덱스 덕분에 전체 테이블 스캔(full table scan) 없이 바로 찾을 수 있음

💡 3️⃣ 인덱스 종류

종류 설명 예시
기본키 인덱스(PK) 기본키 컬럼 자동 생성 id
단일 컬럼 인덱스 특정 컬럼 하나 email
복합 인덱스(Composite Index) 여러 컬럼 조합 (first_name, last_name)
유니크 인덱스(Unique Index) 중복 허용 안 함 email

💡 4️⃣ 주의할 점

  • 인덱스 많다고 무조건 좋은 건 아님
    • 쓰기/수정/삭제 성능이 느려짐 (인덱스도 같이 갱신해야 하므로)
  • 자주 조회하는 컬럼 / WHERE나 JOIN에 자주 쓰이는 컬럼 중심으로 추가

🧠 요약

용어 의미 예시
인덱스(Index) 데이터를 빠르게 검색하도록 돕는 자료 구조 책의 목차
자동 인덱스 PK, UNIQUE 컬럼 id, email(유니크)
수동 인덱스 자주 검색하는 컬럼 추가 CREATE INDEX idx_users_email ON users(email);

 

 

 

Q. 자동 인덱스

“자동 인덱스(Auto Index)”는 DB 설계에서 자주 나오지만, 개념을 명확히 이해하는 게 중요합니다.


💡 1️⃣ 자동 인덱스란?

자동 인덱스(Automatic Index) = DB가 자동으로 생성해주는 인덱스

즉, 개발자가 따로 CREATE INDEX를 하지 않아도 DB가 알아서 만들어주는 인덱스입니다.

 

자동 인덱스는 보통 검색무결성 유지를 위해 필요한 곳에 생성됩니다.


🔍 2️⃣ 자동 인덱스가 생성되는 경우

상황 설명 예시
Primary Key 기본키는 중복 허용 불가 + 검색 빠르게 해야 함 id SERIAL PRIMARY KEY
Unique Key / Unique Constraint 유일성을 보장하기 위해 자동 생성 email VARCHAR(100) UNIQUE
Foreign Key (DB에 따라 다름) 참조 무결성을 위해 내부적으로 생성 author_id INT REFERENCES users(id)

💬 즉, 자동 인덱스는 데이터 무결성과 검색 성능을 위해 DB가 알아서 추가해주는 인덱스입니다.


💡 3️⃣ 예시 — PostgreSQL

 
CREATE TABLE users (
  id SERIAL PRIMARY KEY,         -- 자동 인덱스 생성
  email VARCHAR(100) UNIQUE      -- 자동 인덱스 생성
);
  • id → PK 자동 인덱스
  • email → UNIQUE 자동 인덱스
  • 별도로 CREATE INDEX 안 해도 검색할 때 사용 가능

💡 4️⃣ 특징

  1. 자동 생성 → 개발자가 신경 쓰지 않아도 됨
  2. 주로 PK와 UNIQUE 컬럼 중심
  3. 조회 속도 향상WHERE 절에서 컬럼을 검색할 때 사용
  4. 쓰기 성능 영향 → 데이터 삽입/수정 시 자동 인덱스도 갱신됨

💡 5️⃣ 수동 인덱스와 비교

구분 자동 인덱스 수동 인덱스
생성 주체 DB 개발자
생성 시점 PK/UNIQUE 지정 시 자동 필요 시 CREATE INDEX
용도 무결성 + 검색 조회 성능 최적화
예시 id PRIMARY KEY, email UNIQUE idx_users_created_at

💡 요약하면:

자동 인덱스 = DB가 필수적으로 필요하다고 판단되는 컬럼(PK, UNIQUE 등)에 대해 자동으로 생성하는 인덱스
수동 인덱스 = 개발자가 조회 성능 향상을 위해 직접 추가하는 인덱스

 


 

 


요약

단계 내용
1 API 명세로 리소스 추출
2 엔티티/관계 도출 & ERD 작성
3 정규화 및 비정규화 검토
4 SQL 스키마 설계
5 API ↔ DB 매핑 검증
6 ORM 모델화
7 성능/변경 검증
728x90
반응형