Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions guide/domain-driven-design-Introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
## 1. 도메인(Domain)
**도메인(Domain)** 은 소프트웨어가 해결하고자 하는 **문제 영역**을 의미합니다. 예컨대 쇼핑몰, 은행 시스템, 병원 예약 시스템 등 구체적인 업무나 사업 영역이 도메인이 됩니다. DDD에서는 이 도메인을 깊이 이해하고, 소프트웨어에 적합한 형태로 모델링하여 **비즈니스 로직**을 올바르게 구현하는 것을 최우선으로 삼습니다.

---

## 2. Ubiquitous Language(보편 언어)
**Ubiquitous Language(보편 언어)** 는 **도메인 전문가(현업 담당자)** 와 **개발자** 사이의 소통에 사용되는 **공통 언어**를 말합니다.
- 예를 들어, “장바구니에 상품을 담는다”라는 도메인 용어를 코드 클래스나 메서드 이름에도 그대로 반영하여, 팀 전체가 **같은 개념과 용어로 의사소통**할 수 있도록 돕습니다.
- 이를 통해 서로 다른 직군 사이에서 용어 정의가 불분명해져 생길 수 있는 **지식 누락**이나 **오해**를 줄일 수 있습니다.

---

## 3. Bounded Context(경계 컨텍스트)
도메인이 커지면, 한꺼번에 모든 것을 처리하기 어렵습니다. 그래서 **의미가 구분되는 맥락**별로 나누어 관리하는데, 이를 **Bounded Context(경계 컨텍스트)** 라고 부릅니다.
- 예를 들어, 대형 쇼핑몰 시스템이라면 “상품 관리”, “주문”, “결제” 등으로 나눌 수 있습니다.
- 각 컨텍스트가 **독립적으로** 자체 모델을 유지하되, 필요한 경우에만 다른 컨텍스트와 **명확한 연동 규칙**을 정의합니다.
- 이를 통해 **불필요한 결합**을 피하고, 각 컨텍스트가 독립적으로 발전∙배포될 수 있도록 유연성을 확보합니다.

---

## 4. Entity, Value Object, Aggregate 등 핵심 개념

### 4.1. Entity(엔티티)
- **고유 식별자(ID)** 를 가지며, 해당 식별자를 통해 엔티티의 **생애 주기 전체를 추적**합니다.
- 예: “주문(Order)” 엔티티는 주문 ID로 식별되며, 주문 상태가 바뀌어도 ID가 유지됩니다.

### 4.2. Value Object(값 객체)
- 별도의 식별자가 없고, **값 자체**가 중요한 객체입니다.
- **불변성**을 강조하며, 값이 변경되면 새로운 Value Object로 대체하는 방식을 권장합니다.
- 예: “주소(Address)” 객체는 식별자보다 그 안의 지역, 도로명, 우편번호 등 **값** 자체가 중요합니다.

### 4.3. Aggregate(애그리거트)
- 서로 밀접하게 연관된 **엔티티와 값 객체의 집합**입니다.
- 해당 애그리거트의 **루트 엔티티(Aggregate Root)** 를 통해서만 외부에서 접근∙조작할 수 있도록 하여 일관성을 보장합니다.
- 예: “주문(Order)”과 “주문 항목(OrderLine)”들이 하나의 애그리거트를 이룹니다.

### 4.4. Domain Service(도메인 서비스)
- 특정 엔티티에 속하기 애매하지만, 여전히 **도메인 규칙** 에 해당하는 로직을 모아 둡니다.
- 예: 둘 이상의 애그리거트를 동시에 처리해야 할 경우, 별도의 Domain Service로 추상화하여 책임을 분리할 수 있습니다.

### 4.5. Repository(리포지토리)
- 엔티티나 애그리거트의 **저장∙조회∙삭제**를 책임지는 컴포넌트입니다.
- DDD에서는 Repository를 통해 도메인 객체를 조작하고, 내부적으로는 JPA나 MyBatis 등 구체적인 DB 기술을 사용합니다.

---

## 5. Context Mapping(맥락 맵핑)
**Bounded Context**가 여러 개 존재할 때, 각 컨텍스트 간 **협력 관계**와 **통신 방식**을 정의하는 것을 **Context Mapping**이라고 합니다.
- 예: “주문 컨텍스트”와 “결제 컨텍스트”가 서로 상호작용할 때, 데이터를 어떻게 넘겨주고 어떤 이벤트를 발생시킬지 정의합니다.
- 필요에 따라 **Anti-Corruption Layer**를 두어 외부 시스템∙컨텍스트와의 데이터 변환∙보호를 수행하거나, **Shared Kernel**로 공통 모델을 공유하는 식으로 모델 통합 방식을 결정합니다.

---

## 6. 도메인 이벤트(Domain Event), Event Storming 등
### 6.1. 도메인 이벤트(Domain Event)
- “주문이 생성되었다(OrderCreated)”, “결제가 완료되었다(PaymentCompleted)”처럼, **도메인 로직상 중요한 사건**을 이벤트 객체로 표현합니다.
- 이를 발행∙구독(pub/sub) 구조로 설계하면, 컨텍스트 간 **결합도를 낮추고**, **확장성**을 높일 수 있습니다.

### 6.2. Event Storming
- 도메인 전문가와 개발자가 함께 **시간 축(Time Line)** 순으로 도메인 이벤트를 시각화하여 분석∙설계하는 방법론입니다.
- 포스트잇 등을 활용한 시각화를 통해 **업무 흐름**, **핵심 시나리오**, **잠재적 문제** 등을 빠르게 파악할 수 있습니다.

---

## 7. Subdomain(하위 도메인)
- **Core Subdomain(핵심 하위 도메인)**: 프로젝트에서 가장 중요하고 경쟁력을 만드는 부분입니다. 에너지를 집중 투자해야 합니다.
- **Supporting Subdomain(지원 하위 도메인)**: 핵심을 보조하는 로직이나 기능입니다.
- **Generic Subdomain(범용 하위 도메인)**: 보편적인 기능 예: 결제 모듈, 이메일 전송 등. 필요하다면 제3자 솔루션을 활용하기도 합니다.

DDD는 **핵심 도메인**에 더 많은 시간을 투자해 비즈니스 가치를 최대화하고, 나머지 부분은 효율적으로 구성해 전체 생산성을 높이는 전략을 지향합니다.

---

## 8. DDD가 추구하는 목표 및 장점

1. **복잡한 비즈니스 요구사항에 대한 체계적 접근**
- 핵심적인 문제를 해결하기 위해 도메인을 구조적으로 이해하고, 변화 요구에 대응하기 쉽도록 만듭니다.

2. **팀 간 원활한 소통**
- Ubiquitous Language를 통해 도메인 전문가와 개발자 간 **용어와 개념**이 일치하도록 돕습니다.

3. **올바른 추상화와 캡슐화**
- Aggregate 등을 사용하여 핵심 로직을 감싸고, 불필요한 외부 접근을 제한함으로써 **유지보수**가 쉬워집니다.

4. **확장성과 유연성**
- Bounded Context를 적절히 분리하고, Context Mapping을 통해 **컨텍스트 간 결합도**를 줄여 나갑니다.

---

### 결론
도메인 주도 설계(DDD)는, **도메인 모델**이 개발 과정 전체에서 최우선이 되도록 하여 복잡한 비즈니스 로직을 **정확**하고 **유지보수 가능**하게 만드는 방법론입니다.
도메인 전문가와 개발자가 **공통 언어**로 끊임없이 소통하면서, **도메인 핵심 가치를 소프트웨어에 반영**하려는 노력이 DDD의 근본이라 할 수 있습니다.
183 changes: 183 additions & 0 deletions question/1~3-week-test.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
**1. 의존성 관리가 필요한 이유**

- 라이브러리나 프레임워크가 업데이트되거나 변경될 때, 서로 호환이 보장되도록 버전을 맞추고 충돌을 방지하기 위함
- 코드가 점점 복잡해짐에 따라, 프로젝트의 안정성과 유지보수를 쉽게 하기 위해 필요
- e.g. 도메인 모듈(계층)을 나누면, 한 곳의 수정이 다른 곳에 미치는 영향이 최소화된다.
예를 들어, 도메인 로직을 변경해도 애플리케이션·인프라 레이어는 건드릴 필요가 적다.
각 레이어의 책임이 분명해 유지보수 범위와 충돌이 줄어든다.

---

**2. 계층(레이어)을 구분해서 설계하는 이유**

- 책임 분리를 통해 각 레이어가 맡는 역할을 명확히 하고, 코드 가독성과 유지보수를 향상
- 각 레이어가 독립적으로 변경될 수 있어, 시스템 확장성과 유연성을 높임

---

**3. Controller, Service, Repository 각 레이어는 어떤 책임을 가지며 어떻게 협력하나요?**

- **Controller**: 클라이언트 요청을 받고, 적절한 Service를 호출하여 결과를 반환
- **Service**: 비즈니스 로직을 처리하며, 필요한 경우 Repository를 통해 데이터 접근 수행
- **Repository**: 데이터베이스와 직접 소통하여 데이터를 조회·저장·수정·삭제

---

**4. DDD(Domain-Driven Design)의 목표**

- 소프트웨어의 핵심 복잡도(도메인 로직)를 명확하게 표현하고 해결하기 위함
- 도메인 모델을 중심으로 팀 간의 공통 언어(Ubiquitous Language)를 형성하여 요구사항과 구현이 일치하게 개발

---

**5. OOP(Object-Oriented Programming)가 필요한 이유**

- 객체를 중심으로 책임과 역할을 나누어 설계하면, 코드 재사용성과 유지보수가 좋아짐
- 캡슐화, 상속, 다형성 등을 통해 복잡도를 낮추고 유연성을 높임
- e.g. 회원 정보를 다루는 “User” 객체가 “Name”과 “Address” 같은 값 객체를 각각 캡슐화하여 사용한다고 해보겠습니다.
“Name” 객체가 “firstName”, “lastName”을 유효성 검증(빈 문자열 여부, 특수문자 포함 등)하고, “Address” 객체가 “City”, “Street”, “PostalCode”를 검증하도록 분리하면, 이름 형식 변경이나 주소 포맷 변경 등 세부 규칙이 생겨도 관련 객체만 수정하면 됩니다.
이런 식으로 객체마다 자체적인 검증과 책임을 분명히 두면, 실무에서 규칙이 자주 추가·수정되는 상황에서도 코드를 대폭 갈아엎지 않고도 유연하게 대응할 수 있게 됩니다.

---

**6. .gitignore를 사용하는 목적**

- 버전 관리가 불필요하거나 보안상 노출되면 안 되는 파일(빌드 결과물, 환경 설정 파일 등)을 Git에서 제외하여 관리
- 저장소 용량 낭비와 불필요한 충돌을 방지

---

**7. Docker가 로컬 개발 환경에서 주로 어떻게 쓰이나요?**

- 특정 버전의 DB나 외부 서비스 환경을 빠르게 구성하여, 개발자가 동일한 환경에서 작업할 수 있도록 지원
- 여러 프로젝트에서 환경 충돌 없이 독립적인 컨테이너를 구동할 수 있음

---

**8. Docker의 Image와 Container의 개념과 차이에 대해 설명해주세요.**

- **Image**: 실행 가능한 환경(파일시스템, 런타임, 설정 등)을 캡슐화한 템플릿
- **Container**: Image를 실제로 실행한 상태로, 독립된 프로세스로 동작

---

**9. docker-compose.yml 파일을 작성할 때 유의해야 할 점은 무엇인가요?**

- 서로 연결될 서비스(컨테이너)의 포트, 볼륨, 네트워크 설정을 명확히 정의
- 환경 변수나 데이터 볼륨, 버전 정보를 정확히 기재하여 재현 가능하도록 함
- 여러 컨테이너 간 의존성을 고려하여 실행 순서를 지정하거나, 재시작 정책을 설정

---

**10. JPA가 하는 역할은 무엇인가요?**

- 자바 객체와 데이터베이스 테이블 간의 매핑을 자동으로 처리하여, SQL을 직접 다루지 않고도 데이터를 조작할 수 있게 함
- 데이터 액세스 계층을 단순화하고, 생산성과 유지보수를 높임

---

**11. JPA에서 Entity 클래스에 주로 사용되는 어노테이션과 그 역할을 설명해주세요.**

- `@Entity`: 해당 클래스를 JPA가 관리하는 엔티티로 지정
- `@Table`: 엔티티와 매핑될 테이블 이름 설정
- `@Id`: 엔티티의 PK(기본 키) 필드 지정
- `@GeneratedValue`: PK의 자동 생성 전략 지정
- `@Column`: 테이블의 컬럼 속성(name, nullable, length 등) 지정
- `@OneToMany`, `@ManyToOne`, `@OneToOne`, `@ManyToMany`: 엔티티 간의 연관관계를 설정

---

**12. JPA에서 Converter, Embeddable, Auditing 등을 사용하는 이점은 무엇인가요?**

- **Converter**: 특정 타입을 변환(예: enum ↔ String)하여 DB와 애플리케이션 간에 일관성을 유지
- **Embeddable**: 재사용 가능한 값 객체를 쉽게 활용해 도메인 모델을 풍부하게 표현
- **Auditing**: 생성·수정 시점이나 작성자 등을 자동으로 관리해, 공통 요구사항을 중복 없이 처리

---

**13. 양방향 매핑(ManyToOne, OneToMany) 시 주의해야 할 점은 무엇인가요?**

- 무한 루프(순환 참조) 방지: 예를 들어, 양쪽에서 같은 엔티티를 직렬화할 때
- 연관관계 주인 설정: 어느 쪽이 외래 키를 관리하는 주인인지 명확히 해줘야 함
- 필요 없는 양방향보다는 단방향을 우선 고려하여 복잡도를 낮춤

---

**14. JPA에서 N+1 문제가 무엇인지 예를 간략하게 적고 해결방법을 설명하세요.**

- **N+1 문제**: 연관 관계가 있는 엔티티를 조회할 때, 추가로 N번의 쿼리가 발생하여 성능 저하를 일으키는 문제
- **예시**: Member와 Team 엔티티가 다대일(ManyToOne) 관계일 때, 모든 Member를 조회하고 각 Member의 Team을 LAZY 로딩할 때 발생
```java
public void demonstrateNPlusOneProblem() {
// 첫 번째 쿼리: 모든 Member를 조회
// 예) SELECT m FROM Member m
List<Member> members = memberRepository.findAll(); // 1개의 쿼리

for (Member m : members) {
// 각 Member마다 Team 정보를 LAZY 로딩하면서 실행되는 쿼리
// 예) SELECT t FROM Team t WHERE t.id = ?
System.out.println(m.getTeam().getName()); // N개의 쿼리
}
}
```

- **해결 방법**
- 페치 조인(Fetch Join)을 사용하여 한 번의 쿼리로 모든 연관 엔티티를 함께 조회
- JPA의 `EntityGraph`를 사용하여 페치 조인을 명시적으로 설정
- @BatchSize를 사용하여 일괄 처리로 성능을 향상

**15. 단위 테스트(Unit Test)는 무엇이며, 왜 필요한가요?**

- **정의**: 시스템의 가장 작은 단위(주로 메서드나 클래스)의 동작을 검증하는 테스트
- **필요성**: 빠른 피드백으로 에러를 조기에 발견하고, 리팩토링이나 기능 추가 시 안정성을 제공

---

**16. 통합 테스트(Integration Test)는 무엇이며, 왜 필요한가요?**

- **정의**: 여러 컴포넌트나 모듈이 실제로 연결된 상태에서 전체 흐름이 정상 동작하는지 검증하는 테스트
- **필요성**: 모듈 간 연동 문제, 의존성 문제 등을 조기에 발견해 실제 배포 후 장애를 줄임

---

**17. Mock은 무엇이며, 어떤 상황에서 왜 사용하는가요?**

- **정의**: 실제 객체 대신에 비슷한 동작만 흉내내는 ‘가짜 객체’
- **사용 상황**: 외부 의존성이 있는 코드를 테스트할 때, 네트워크나 DB 같은 복잡한 환경을 대체하여 테스트를 빠르고 독립적으로 진행하기 위해 사용

---

**18. RESTful한 API를 설계할 때 핵심적으로 고려해야 할 점들은 무엇인가요?**

- 리소스(명사형) 중심의 URL과 HTTP 메서드(POST, GET, PUT, DELETE)로 명확한 액션 표현
- 적절한 상태 코드(2xx, 4xx, 5xx)와 에러 메시지 설계
- 요청과 응답에 일관성 있고 명확한 JSON(or XML) 구조 사용
- 보안, 버전 관리, 문서화 등도 함께 고려
- 리소스 간의 관계를 표현하고, 가능하다면 HATEOAS(Hypermedia as the Engine of Application State)를 활용해 API의 자기 기술적인 특성을 활용

---

**19. DTO를 사용하는 이유는 무엇인가요?**

- 도메인(Entity) 객체와 API 요청·응답 모델을 분리해, 불필요하거나 민감한 정보 노출을 막을 수 있음
- 계층 간 데이터 이동 시 변환 로직을 추가하거나, 표시 형식을 자유롭게 조절 가능

---

**20. Spring Boot에서 전역 예외 처리를 위해 사용할 수 있는 방법은 무엇인가요?**

- `@ControllerAdvice`와 `@ExceptionHandler`를 사용해 전역적으로 예외를 핸들링
- `ResponseEntityExceptionHandler`를 상속받아 공통 에러 처리를 구성
- 필터나 인터셉터 등을 활용해 더 넓은 범위에서 에러 처리를 할 수도 있음

---

**21. API 서버로 들어오는 다양한 데이터의 포맷(타입)을 핸들링하기 위해 어떤 방법들을 사용할 수 있나요?**

- RequestParam 혹은 PathVariable의 경우
- **Spring**에서 `HttpMessageConverter`를 설정하거나 커스텀 Converter를 구현
- RequestBody의 경우
- **Jackson**이나 **Gson** 같은 라이브러리를 활용해 JSON/XML 등 다양한 포맷을 직렬화/역직렬화
- 커스텀 Serializer/Deserializer를 작성해 특정 데이터 타입에 대해 맞춤형 처리를 적용

---
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,14 @@

@Getter
@NoArgsConstructor
//@Table(name = "member")
@Entity
public class Member {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "`key`")
private Integer key;

@Convert(converter = MemberCodeConverter.class)
@Column(name = "`code`")
private MemberCode code;

@Embedded
Expand Down