
DB를 그냥 막 만들면 나중에 데이터가 중복되고, 쿼리가 느려지고, 고치려면 테이블을 다 갈아엎어야 하는 상황이 온다. 처음부터 잘 설계해두면 이런 일이 없다. 핵심부터 한 줄로 정리하면 이렇다.
데이터베이스 설계 = 데이터를 어떤 테이블에, 어떤 구조로, 어떻게 연결해서 저장할지 미리 계획하는 것
서류 캐비닛 설계라고 생각하면 됨! 회사 서류를 캐비닛에 정리할 때, 서랍을 어떻게 나누고 어떤 라벨을 붙일지 먼저 계획하는 거다. 계획 없이 막 넣으면 나중에 "그 서류 어디 있지?" 할 때 캐비닛 전체를 다 뒤져야 한다.
잘 설계된 캐비닛은 원하는 서류를 바로 찾을 수 있고, 새 서류 추가도 쉬운 것 같음!
1단계 — 개념적 설계 (Conceptual Design)
"뭘 저장할 거냐"를 추상적으로 정리하는 단계. ERD(Entity-Relationship Diagram) 를 그린다.
2단계 — 논리적 설계 (Logical Design)
ERD를 실제 테이블 구조로 변환. 테이블명, 컬럼명, 데이터 타입, 키를 정한다.
3단계 — 물리적 설계 (Physical Design)
어떤 DB에서 어떻게 저장할지. 인덱스, 파티셔닝, 스토리지 엔진 같은 것들을 결정한다.
테이블끼리 연결 방식이 세 가지다.
1:1 (일대일)
1:N (일대다)
user_id 컬럼(외래키)을 넣어서 연결N:M (다대다)
유저 테이블 ←→ 유저_태그 테이블 ←→ 태그 테이블중복을 없애고 구조를 깔끔하게 만드는 과정. 단계가 있다.
쉽게 말하면 "중복 데이터 없애기" 가 정규화의 핵심이다. 같은 정보가 여러 테이블에 흩어져 있으면, 하나 바꿀 때 다 찾아서 바꿔야 해서 실수가 난다.
정규화를 일부러 풀기도 한다. 정규화된 구조는 깔끔하지만, 조회할 때 JOIN이 많아져서 느려질 수 있기 때문이다. 읽기 성능이 정말 중요한 경우에 중복을 허용하고 JOIN을 줄이는 전략이다. 설계할 때는 정규화로 시작하고, 성능 문제가 생기면 그때 비정규화를 고려하는 게 순서 같음!
DB 설계에서 인덱스도 미리 계획해야 한다.
email, user_id)인덱스가 없으면 풀 스캔(처음부터 끝까지 다 훑기)이 일어나서 데이터 많아질수록 느려짐. 단, 인덱스가 많아지면 쓰기 성능이 떨어지니까 남발하면 안 됨 같음!
간단한 투표 서비스 DB 설계를 예로 들면 이렇다.
[users]
- id (PK)
- email
- nickname
- created_at
[polls]
- id (PK)
- user_id (FK → users.id) ← 누가 만들었나
- title
- description
- created_at
[options]
- id (PK)
- poll_id (FK → polls.id) ← 어느 투표 항목인가
- content
[votes]
- id (PK)
- user_id (FK → users.id) ← 누가 투표했나
- option_id (FK → options.id) ← 어느 항목에 투표했나
- created_at
여기서 votes 테이블이 users와 options의 N:M 관계를 해결하는 중간 테이블이다. "유저는 여러 투표를 할 수 있고, 항목은 여러 유저에게 선택될 수 있으니까" 딱 N:M 관계인 거다.
자주 하는 실수는 options 컬럼을 콤마로 한 셀에 넣는 거다("선택1,선택2,선택3"). 이러면 나중에 각 옵션 별 투표 수 집계하려면 파싱부터 해야 해서 굉장히 불편해짐. 처음부터 테이블 나눠두는 게 맞다 같음!
| 구분 | 내용 |
|---|---|
| 한 줄 정의 | 데이터를 어떤 구조로 저장할지 미리 계획하는 것 |
| 비유 | 서류 캐비닛 서랍 설계 |
| 설계 3단계 | 개념적 → 논리적 → 물리적 |
| 관계 종류 | 1:1 / 1:N / N:M (중간 테이블) |
| 정규화 | 중복 없애기. 성능 필요하면 비정규화 고려 |
| 인덱스 | 자주 검색·조건·JOIN 컬럼에 걸기 |
쉽게 외우면 "테이블을 어떻게 나누고 연결할지 미리 그림 그리는 것" — ERD 한 장 그려두면 개발하다 방향 잃을 일이 없어지는 것 같음!
버튼 하나, 인풋 하나도 그냥 만들면 나중에 유지보수가 힘들어진다. 컴포넌트를 어떻게 나누고, 어떤 props를 줄지, 어떻게 재사용할지를 미리 고민하는 게 UI 컴포넌트 설계다. 핵심부터 한 줄로 정리하면 이렇다.
UI 컴포넌트 설계 = 화면을 구성하는 UI 조각들을 재사용 가능하고, 일관성 있고, 유지보수하기 쉽게 구조화하는 것
레고 블록 설계라고 생각하면 됨! 집(화면)을 만들 때마다 벽돌을 처음부터 새로 만들면 비효율적이다. 규격화된 레고 블록(컴포넌트)을 미리 잘 만들어두면, 다른 집 만들 때도 그 블록을 꺼내서 조립하면 된다.
잘 설계된 블록은 어디서든 끼워 맞출 수 있고, 한 블록 고쳐도 다른 블록에 영향이 없는 것 같음!
모든 걸 컴포넌트로 만들 필요도 없고, 너무 안 나눠도 안 된다. 이럴 때 나누면 됨:
컴포넌트를 5단계 계층으로 나누는 방법론이다.
Atoms (원자)
→ Molecules (분자)
→ Organisms (유기체)
→ Templates (템플릿)
→ Pages (페이지)
비유: Atoms는 밀가루·달걀, Molecules는 반죽, Organisms는 케이크 한 조각, Templates는 케이크 틀, Pages는 완성된 케이크 같음!
컴포넌트의 "입력 인터페이스"다. 잘 설계된 props는 이런 특징이 있다.
명확한 타입 정의 (TypeScript)
interface ButtonProps {
label: string
variant?: "primary" | "secondary" | "danger"
size?: "sm" | "md" | "lg"
disabled?: boolean
onClick?: () => void
}
? 유무)"primary" | "secondary")Props 설계 원칙
onClick, 변경 핸들러는 onChange처럼 네이밍 통일children을 잘 활용하면 유연한 컴포넌트를 만들 수 있음컴포넌트를 나눌 때 "이 상태가 어디에 있어야 하나" 도 같이 고민해야 한다.
useState로 해결Presentational vs Container 패턴
Container 컴포넌트 → 데이터 가져오고, 로직 처리
↓ props
Presentational 컴포넌트 → 받은 데이터 그냥 보여주기만 함
Presentational 컴포넌트는 순수하게 UI만 담당해서 재사용이 쉽고 테스트도 쉬워진다.
Compound Component 패턴
관련된 컴포넌트를 그룹으로 묶는 패턴. 라이브러리에서 자주 보임.
<Select>
<Select.Trigger />
<Select.Options>
<Select.Option value="a">옵션 A</Select.Option>
</Select.Options>
</Select>
버튼 컴포넌트 하나 제대로 설계하는 것만 봐도 차이가 확 보인다.
나쁜 예 — 상황마다 따로 만들기
<BlueButton />
<RedButton />
<SmallBlueButton />
<LargeRedButton />
→ 버튼 색이나 크기 바꿀 때 파일 여러 개 고쳐야 함
좋은 예 — props로 유연하게
<Button variant="primary" size="lg" onClick={handleSubmit}>
제출
</Button>
<Button variant="danger" size="sm" disabled>
삭제
</Button>
→ Button 컴포넌트 하나만 관리하면 됨. 스타일 바꾸면 전체 적용
폴더 구조도 설계의 일부다.
components/
├── ui/ ← Atoms (Button, Input, Badge...)
├── common/ ← Molecules (SearchBar, FormField...)
├── features/ ← 기능별 Organisms (PollCard, VoteForm...)
└── layouts/ ← 레이아웃 관련
이렇게 잡아두면 팀원이 "버튼 어디 있어요?" 할 때 components/ui/ 보면 되고, "투표 카드 어디 있어요?" 할 때 components/features/ 보면 된다 같음!
| 구분 | 내용 |
|---|---|
| 한 줄 정의 | UI 조각을 재사용 가능하고 일관성 있게 구조화하는 것 |
| 비유 | 레고 블록 규격 설계 |
| 나누는 기준 | 2번 이상 반복 / 역할이 명확히 다를 때 |
| 대표 방법론 | 아토믹 디자인 (Atoms → Pages) |
| Props 핵심 | 명확한 타입 + 유니온으로 허용값 제한 |
| 상태는? | 쓰이는 범위에 따라 로컬 / 공유 / 서버 상태 구분 |
쉽게 외우면 "나중에 고치기 쉽고, 어디서든 꺼내 쓸 수 있게 컴포넌트를 설계하는 것" — 처음엔 귀찮지만 페이지가 10개 넘어가는 순간 설계해둔 게 빛나는 것 같음!