diff --git a/keyword/chaptor01/keyword.md b/keyword/chapter01/keyword.md similarity index 100% rename from keyword/chaptor01/keyword.md rename to keyword/chapter01/keyword.md diff --git a/keyword/chaptor02/keyword.md b/keyword/chapter02/keyword.md similarity index 100% rename from keyword/chaptor02/keyword.md rename to keyword/chapter02/keyword.md diff --git a/keyword/chapter03/keyword.md b/keyword/chapter03/keyword.md new file mode 100644 index 0000000..009061f --- /dev/null +++ b/keyword/chapter03/keyword.md @@ -0,0 +1,227 @@ +# Q1. SOLID 원칙이란? + +객체지향 설계의 5가지 핵심 원칙 ⇒ 더 좋은 소프트웨어를 만들기 위함 + +--- + +## 1) S - Single Responsibility Principle (단일 책임 원칙) + +"하나의 클래스는 하나의 책임만 가져야 한다." + +예를 들어, `UserService`에서는 사용자 관련 기능들만 관리해야 한다는 것이다. +`UserService`에서 Email, Auth, Review 등 다른 로직을 포함하지 않고 각각의 클래스로 분리해야 한다. + +--- + +## 2) O - Open/Closed Principle (개방-폐쇄 원칙) + +"확장에는 열려 있고, 수정에는 닫혀 있어야 한다." + +새로운 기능을 추가할 때 기존 코드를 수정하지 않은 채로 개발을 진행할 수 있도록 설계해야 한다. +기존 코드를 직접 수정하지 않기 위해서 인터페이스 및 추상화 방식을 많이 사용한다. + +> 💡 이번 워크북 중 DI, IoC는 '개방-폐쇄 원칙'을 지킨 기능이라고 볼 수 있다. + +--- + +## 3) L - Liskov Substitution Principle (리스코프 치환 원칙) + +"자식 클래스는 부모 클래스를 완전히 대체할 수 있어야 한다." + +- 부모 클래스 = `Rectangle` (직사각형) / 자식 클래스 = `Square` (정사각형) +- 이때, 자식 클래스는 부모 클래스를 완전히 대체할 수 있는가? ❌ +- 가로 5, 세로 3인 경우를 자식 클래스인 `Square`는 받을 수 없음 + +즉, LSP는 `"is-a"` 관계가 코드에서도 성립하는지 따져보는 것이 필요하다. + +--- + +## 4) I - Interface Segregation Principle (인터페이스 분리 원칙) + +인터페이스는 자신이 사용하지 않는 메서드를 포함하지 않도록 설계한다. + +즉, 범용적으로 여러 기능을 가진 인터페이스가 아니라, +여러 개의 구체적이고 작은 범위의 인터페이스로 설계한다. + +--- + +## 5) D - Dependency Inversion Principle (의존성 역전 원칙) + +클래스가 의존 관계를 설정할 때, 구체 클래스가 아닌 추상 클래스(인터페이스)에 의존해야 한다. + +예를 들어 `UserService`가 Repository를 의존할 때, +`JpaRepository`(구체 클래스)가 아닌 `Repository` 자체에 의존하도록 설정하고, +인터페이스를 통해 실제 객체를 주입받는 방식으로 설계해야 한다. + +--- + +# Q2. DI란? + +**의존성 주입(Dependency Injection)**이란 객체가 필요로 하는 의존 객체를 코드 내부에서 직접 생성하지 않고(`new`) 외부에서 생성하여 주입받는 방식이다. +주입 방법으로는 생성자 주입, Setter 주입, 필드 주입이 있으며, Spring에서는 생성자 주입을 권장한다. + +Spring에서는 IoC 컨테이너가 Bean의 생성과 의존성 주입을 자동으로 관리하며, +개발자는 어노테이션(`@Component`, `@Service` 등)으로 Bean을 등록하면 컨테이너가 이를 수행한다. + +생성자가 인터페이스 타입으로 의존성을 받기 때문에 다형성이 활용되며, +내부 코드 수정 없이 구현체를 교체할 수 있어 OCP를 준수할 수 있다. + +--- + +# Q3. IoC란? + +**IoC(Inversion of Control, 제어의 역전)**란 객체의 생성과 의존관계 설정의 제어권을 +개발자가 아닌 프레임워크(Spring)가 갖는 설계 원칙이다. + +전통적으로는 개발자가 `new`로 객체를 직접 생성하고 의존성도 직접 연결했지만, +IoC에서는 개발자가 어노테이션(`@Component`, `@Service` 등)으로 Bean을 선언만 하면 +Spring의 IoC 컨테이너가 객체의 생성, 의존성 주입, 생명주기 관리를 자동으로 수행한다. + +DI는 IoC를 구현하는 대표적인 방법으로, IoC 원칙을 실현하는 구체적인 방법이다. + +> 💡 **IoC vs DI** +> - **IoC** : 제어권을 프레임워크에 넘기는 **원칙** +> - **DI** : 그 원칙을 실현하는 **구체적인 방법** + +--- + +# Q4. 생성자 주입 vs 수정자 주입 vs 필드 주입 + +## 1) 필드 주입 + +`@Autowired`를 필드에 직접 선언하는 방식으로, 코드가 간결하지만 +`final` 선언이 불가능하고 Spring 컨텍스트 없이는 의존성을 주입할 방법이 없어 +순수 Java 단위 테스트가 어렵다. +또한 어떤 의존성이 필요한지 외부에서 파악하기 어려워 의존성이 숨겨진다는 단점이 있다. + +## 2) Setter 주입 + +의존성을 선택적으로 주입할 수 있다는 점에서 유연하지만, +Setter 호출 없이도 객체가 생성되기 때문에 의존성이 누락된 채로 동작할 위험이 있다. +또한 외부에서 언제든 Setter를 호출해 의존성을 교체할 수 있어 불변성을 보장하지 못한다. + +## 3) 생성자 주입 ✅ + +객체 생성 시점에 모든 의존성을 한 번에 주입하기 때문에 +`final` 선언으로 불변성을 보장하고, 의존성이 누락되면 객체 자체가 생성되지 않아 +필수 의존성을 강제할 수 있다. +또한 순환참조가 발생하면 애플리케이션 시작 시점에 즉시 감지되며, +Spring 없이도 순수 Java로 단위 테스트가 가능하다. +Spring이 생성자 주입을 공식적으로 권장하는 이유가 여기에 있다. + +--- + +| | 생성자 주입 | Setter 주입 | 필드 주입 | +|---|---|---|---| +| `final` 선언 | ✅ | ❌ | ❌ | +| 순환참조 감지 | 시작 시점 | 런타임 | 런타임 | +| 단위 테스트 | ✅ 순수 Java | ✅ 가능 | ❌ Spring 필요 | +| 누락 의존성 | 생성 불가 | 런타임 NPE | 런타임 NPE | +| 의존성 명시성 | ✅ 명확 | ✅ 명확 | ❌ 숨겨짐 | +| 사용 권장 | ✅ 권장 | 선택적 의존성에만 | ❌ 지양 | + +--- + +# Q5. AOP란? + +**AOP(Aspect-Oriented Programming, 관점지향 프로그래밍)**란 트랜잭션 관리, 로깅, 인증 등 +여러 객체에 공통적으로 사용되는 부가기능을 핵심 비즈니스 로직과 분리해 모듈화하는 프로그래밍 방법이다. +이를 통해 핵심 로직의 가독성을 높이고 중복 코드를 줄일 수 있다. + +--- + +## AOP의 동작 원리 + +AOP는 프록시 패턴 기반으로 동작한다. +Spring 컨테이너가 `@Aspect`가 붙은 클래스를 감지해 대상 Bean의 프록시 객체를 생성하고, +메서드 호출 시 실제 객체 대신 프록시가 먼저 실행되어 부가기능(Advice)을 수행한 뒤 핵심 로직으로 위임한다. + +주요 구성 요소로는 부가기능을 모듈화한 **Aspect**, 적용 대상 메서드를 지정하는 **Pointcut**, +실제 부가기능 코드인 **Advice**가 있다. +Advice는 실행 시점에 따라 `@Before`, `@After`, `@Around`로 구분된다. + +- `@Before`: 메서드 실행 전 +- `@After`: 메서드 실행 후 +- `@Around`: 메서드 실행 전 후 모두 담당 (가장 많이 사용) + +--- + +# Q6. 서블릿이란? + +**Servlet**이란 Java EE(현재 Jakarta EE) 표준 스펙에 정의된, 프로토콜을 처리하기 위한 인터페이스이다. +본질적으로 Servlet은 웹 서버 ↔ Java Application 사이의 계약이다. +Java에서 "웹 요청을 처리하는 컴포넌트는 이 Servlet이라는 인터페이스를 구현해야 한다."고 표준화한 것이다. + +--- + +## 왜 만들어졌는가? + +웹 서버는 원래 정적인 HTML 파일만 반환할 수 있었다. +그러나 사용자마다 다른 응답을 줘야 하는 동적 처리(로그인, 마이페이지, 검색 결과 등)가 필요해졌고, +이를 위해 서블릿이 만들어졌다. + +그리고 Servlet은 Servlet Container 없이 동작할 수 없다. + +> **Servlet Container**란 실제 Servlet을 생성하고 관리하고 호출을 담당하는 컨테이너 +``` +TCP 소켓 연결 +→ HTTP 요청 메시지 파싱 +→ ServletRequest, ServletResponse 구현 객체 생성 +→ URL 패턴에 맞는 Servlet 탐색 +→ Servlet이 없으면 init() 호출로 인스턴스 생성 (최초 1회) +→ service() 호출 +→ 응답 완료 후 소켓 처리 +→ 애플리케이션 종료 시 destroy() 호출 +→ TCP 연결 해제 +``` + +--- + +## HttpServlet이란? + +**HttpServlet**이란 Servlet 인터페이스를 HTTP에 특화해 구현한 추상 클래스이다. + +### 상속 구조 + +1. `Servlet` (인터페이스) +2. `HttpServlet` (추상 클래스 - HTTP 특화) +3. `DispatcherServlet` (Spring MVC에서 사용) + +HttpServlet의 핵심 역할은 `service()` 메서드 안에서 HTTP 메서드 (GET, POST, PUT, DELETE)를 +분기해주는 것이다. +또한 `ServletRequest`, `ServletResponse` 대신 HTTP 전용 기능이 들어있는 +`HttpServletRequest`, `HttpServletResponse`를 사용한다. + +--- + +## DispatcherServlet이란? + +**DispatcherServlet**이란 Spring이 만든 HttpServlet의 구현체이다. + +핵심 역할은 **Front Controller 패턴의 구현**이다. +모든 URL 요청을 DispatcherServlet에서 한 번에 받고 내부적으로 라우팅하는 패턴이다. +기존의 방식은 요청 100개가 오면 Servlet 클래스를 100개 만들어야 했고, +공통 기능(인증 등)을 중복으로 전부 작성했어야 했다. + +--- + +## 서블릿 동작 과정 - Spring MVC +``` +클라이언트 HTTP 요청 +→ Tomcat(서블릿 컨테이너) - TCP 연결 / HTTP 파싱 / Request, Response 객체 생성 후 전달 +→ DispatcherServlet - Front Controller 패턴으로 모든 요청을 여기서 전부 받음 +→ HandlerMapping - 요청 URL을 보고 어떤 Controller의 어떤 메서드에서 처리할지 결정 +→ HandlerAdapter - Controller를 실제로 수행할 수 있는 형태로 변환하고, + @RequestParam, @RequestBody, @PathVariable 등의 + 파라미터 바인딩을 처리한 뒤 메서드를 호출한다. +→ Controller - 비즈니스 로직 수행 +→ Service ↔ Repository ↔ DB +→ ViewResolver / MessageConverter + - ViewResolver : HTML 템플릿 파일 찾아서 렌더링 + - MessageConverter : 객체를 JSON으로 직렬화 +→ HTTP 응답 반환 +``` + +> 💡 **핵심 포인트 : 관심사 분리** +> 개발자는 Controller 안의 비즈니스 로직만 개발하면 되고, +> 나머지 모든 과정은 Spring이 대신 처리해준다. \ No newline at end of file diff --git "a/mission/chaptor01/images/1\354\243\274\354\260\250_\354\240\234\354\235\264\354\235\230_\354\234\240\353\246\254_\355\224\274\354\226\264\353\246\254\353\267\260.jpg" "b/mission/chapter01/images/1\354\243\274\354\260\250_\354\240\234\354\235\264\354\235\230_\354\234\240\353\246\254_\355\224\274\354\226\264\353\246\254\353\267\260.jpg" similarity index 100% rename from "mission/chaptor01/images/1\354\243\274\354\260\250_\354\240\234\354\235\264\354\235\230_\354\234\240\353\246\254_\355\224\274\354\226\264\353\246\254\353\267\260.jpg" rename to "mission/chapter01/images/1\354\243\274\354\260\250_\354\240\234\354\235\264\354\235\230_\354\234\240\353\246\254_\355\224\274\354\226\264\353\246\254\353\267\260.jpg" diff --git "a/mission/chaptor01/images/umc_0\354\243\274\354\260\250_erd\354\204\244\352\263\204_\354\210\230\354\240\225\353\263\270.jpg" "b/mission/chapter01/images/umc_0\354\243\274\354\260\250_erd\354\204\244\352\263\204_\354\210\230\354\240\225\353\263\270.jpg" similarity index 100% rename from "mission/chaptor01/images/umc_0\354\243\274\354\260\250_erd\354\204\244\352\263\204_\354\210\230\354\240\225\353\263\270.jpg" rename to "mission/chapter01/images/umc_0\354\243\274\354\260\250_erd\354\204\244\352\263\204_\354\210\230\354\240\225\353\263\270.jpg" diff --git a/mission/chaptor01/mission.md b/mission/chapter01/mission.md similarity index 100% rename from mission/chaptor01/mission.md rename to mission/chapter01/mission.md diff --git "a/mission/chaptor02/images/2\354\243\274\354\260\250_\355\224\274\354\226\264\353\246\254\353\267\260_\354\202\254\354\247\204.jpg" "b/mission/chapter02/images/2\354\243\274\354\260\250_\355\224\274\354\226\264\353\246\254\353\267\260_\354\202\254\354\247\204.jpg" similarity index 100% rename from "mission/chaptor02/images/2\354\243\274\354\260\250_\355\224\274\354\226\264\353\246\254\353\267\260_\354\202\254\354\247\204.jpg" rename to "mission/chapter02/images/2\354\243\274\354\260\250_\355\224\274\354\226\264\353\246\254\353\267\260_\354\202\254\354\247\204.jpg" diff --git a/mission/chaptor02/mission.md b/mission/chapter02/mission.md similarity index 100% rename from mission/chaptor02/mission.md rename to mission/chapter02/mission.md diff --git "a/mission/chapter03/images/3\354\243\274\354\260\250_\354\212\244\355\224\204\353\247\201_\355\235\220\353\246\204_\355\201\260\352\267\270\353\246\274.jpg" "b/mission/chapter03/images/3\354\243\274\354\260\250_\354\212\244\355\224\204\353\247\201_\355\235\220\353\246\204_\355\201\260\352\267\270\353\246\274.jpg" new file mode 100644 index 0000000..cc99ff1 Binary files /dev/null and "b/mission/chapter03/images/3\354\243\274\354\260\250_\354\212\244\355\224\204\353\247\201_\355\235\220\353\246\204_\355\201\260\352\267\270\353\246\274.jpg" differ diff --git "a/mission/chapter03/images/3\354\243\274\354\260\250_\355\224\274\354\226\264\353\246\254\353\267\260_\354\202\254\354\247\204.jpg" "b/mission/chapter03/images/3\354\243\274\354\260\250_\355\224\274\354\226\264\353\246\254\353\267\260_\354\202\254\354\247\204.jpg" new file mode 100644 index 0000000..563bda9 Binary files /dev/null and "b/mission/chapter03/images/3\354\243\274\354\260\250_\355\224\274\354\226\264\353\246\254\353\267\260_\354\202\254\354\247\204.jpg" differ diff --git a/mission/chapter03/mission.md b/mission/chapter03/mission.md new file mode 100644 index 0000000..09726b1 --- /dev/null +++ b/mission/chapter03/mission.md @@ -0,0 +1,82 @@ +## 피어리뷰 - 유리의 워크북 캡쳐 + +![3주차_제이의_유리_피어리뷰.jpg](./images/3주차_피어리뷰_사진.jpg) + +## 제이 > 유리 피어리뷰 + +> AOP 개념이 낯설어서 텍스트만으로 이해하기가 좀 어려웠는데 그림을 통해 직관적으로 이해하는데 도움이 많이 됐습니다. +> Class A, Class B, Class C에 반복해서 사용 중인 메서드를 색깔별로 구분해 놓았고, 이를 각 Class에서 완전히 분리하여 Aspect X, Aspect Y, Aspect Z로 묶고 각 Aspect 안에 사용하는 클래스 명을 정리한 그림이 인상 깊었습니다. + +--- +# [Spring 요청/응답 전체 흐름] +![3주차_스프링_흐름_큰그림.jpg](./images/3주차_스프링_흐름_큰그림.jpg) + +--- + +## 영역 1 : Servlet Container (Tomcat) +``` +가장 바깥 영역. 클라이언트의 HTTP 요청이 가장 먼저 도착하는 곳. + +**Tomcat** : Java 프로세스로 실행되는 서버. +TCP 소켓을 연결하고 클라이언트 요청이 오면 HTTP 메시지를 파싱해서 +`HttpServletRequest`, `HttpServletResponse` 객체를 생성한다. + +Tomcat이 `filterChain.doFilter()`를 호출하면 Filter들이 실행된다. +``` +--- + +## 영역 2 : Filter Chain +``` +Tomcat이 HTTP 메시지를 파싱한 후, Spring Container에 도달하기 전에 통과하는 영역. +Filter는 Spring 내부가 아닌 바깥 영역이다. + +Filter Chain은 여러 Filter가 체인처럼 연결된 구조이다. +Filter 1 → Filter 2 → Filter 3 순서로 진행되고, +각 Filter 내부 `doFilter()` 메서드에서 `chain.doFilter()`를 호출해야 다음 Filter로 넘어가는 방식이다. +``` +### Filter의 주 기능 +``` +- 인코딩 설정 (UTF-8) +- CORS 처리 +- XSS 방어 +- JWT 토큰 파싱 (Spring Security도 Filter 기반으로 동작함) + +모든 Filter가 완료되면, 이 시점에 Tomcat이 `DispatcherServlet.service()`를 호출한다. +``` +--- + +## 영역 3 : Spring Container + +FilterChain을 모두 통과하고 Tomcat이 `service()`를 호출하면 +Spring Container 속 DispatcherServlet으로 진입한다. +``` +- `DispatcherServlet.doDispatch()` 시작 +- `HandlerMapping.getHandler()` : "어떤 Handler가 처리할지 알려줘" → `HandlerExecutionChain` 반환 (Handler + Interceptor 목록) +- `Interceptor.preHandle()` : 인증/인가 체크 목적으로 사용 (`boolean` 값으로, `false`가 반환되면 Handler(Controller) 진입을 막음) +- `HandlerAdapter.handle()` : 파라미터 바인딩 진행 후 → Handler(Controller) 메서드 실행 +- `Interceptor.postHandle()` : ModelAndView를 수정할 수 있는 단계 (REST API에서는 잘 안 씀) +- `ViewResolver` : Handler가 반환한 View 이름을 실제 템플릿 파일로 변환 후 렌더링 진행. + 만약 `@RestController`이면 ViewResolver 대신 MessageConverter가 동작한다. + 객체를 Jackson으로 JSON 직렬화해서 응답 바디에 사용함 +- `Interceptor.afterCompletion()` : 모든 처리가 끝난 후 호출됨. 로그 기록 / DB 커넥션 반납 등에 사용 +``` +--- + +## Interceptor.afterCompletion() 완료 이후 → Client 이동 과정 +``` +afterCompletion() +↓ +doDispatch() 종료 +↓ +service() 종료 +↓ +Filter N (chain.doFilter() 이후 코드 실행) ← 역순 시작 +↓ +Filter 2 (chain.doFilter() 이후 코드 실행) +↓ +Filter 1 (chain.doFilter() 이후 코드 실행) +↓ +Tomcat이 HTTP 응답 메시지 작성 후 전송 +↓ +클라이언트 +``` \ No newline at end of file