From 4e2c969d6db1550bf23587a423822da395bfcd4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=9C=A4=EC=A3=BC?= Date: Sat, 29 Mar 2025 22:16:53 +0900 Subject: [PATCH 1/7] =?UTF-8?q?[1=EC=A3=BC=EC=B0=A8]=20{=EC=A0=95=EC=9C=A4?= =?UTF-8?q?=EC=A3=BC}=20=ED=95=99=EC=8A=B5=20PR=20=EC=A0=9C=EC=B6=9C?= =?UTF-8?q?=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...{\354\240\225\354\234\244\354\243\274}.md" | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 "week1/[1\354\243\274\354\260\250] {\354\240\225\354\234\244\354\243\274}.md" diff --git "a/week1/[1\354\243\274\354\260\250] {\354\240\225\354\234\244\354\243\274}.md" "b/week1/[1\354\243\274\354\260\250] {\354\240\225\354\234\244\354\243\274}.md" new file mode 100644 index 0000000..c7eb907 --- /dev/null +++ "b/week1/[1\354\243\274\354\260\250] {\354\240\225\354\234\244\354\243\274}.md" @@ -0,0 +1,47 @@ +# 1주차 학습 내용 +객체지향적으로 코드를 작성해보는게 처음이라 역할을 어떻게 분리해야 할 지 고민하는데 +많은 시간을 쓰다보니 아직 코드 구현을 다 하지 못했습니다. +객체지향적으로 구현하려고 노력했지만, 잘 지키지 못한 것 같습니다. 다음 과제에서는 +더 나은 코드를 작성할 수 있도록 노력하겠습니다. + +또한, MVC패턴에 대해서 정확히 이해하지 못했었는데, 이번 과제를 통해 +각 구성 요소가 어떤 역할을 하는지 조금은 더 이해할 수 있게 된 것 같습니다. +--- +### Stream +- 데이터 흐룸 속에서 각각의 원하는 값을 가공하여 최종 소비자에게 제공하는 역할 +- 불필요한 코딩(for, if) 없이 구현 가능 +- 주로 Collection, Arrays에 사용됨 +- 구조 + - Stream 생성 + - 중간 연산 + - 최종 연산 +- Collections객체 집합.스트립 생성().중간연산().최종연산() +- 파이프라인: .으로 연계할 수 있게 하는 방법 +- 스트림 생성 + - 배열을 stream 형식으로 입력 받았을 때: Arrays.stream(배열) + - 객체를 stream 형식으로 입력 받았을 때: 객체.stream() + ```Java + winningNumbers = Arrays.stream(inputWinningNumbers.split(",")) + .map(String::trim) + .mapToInt(Integer::parseInt) + .boxed() + .collect(Collectors.toList()); + ``` +- inputWinningNumbers 문자열을 쉼표로 분리하여 스트림 생성(데이터 소스) +- .map(String::map): 스트림의 각 문자열 요소의 공백 제거(중간 연산) +- .mapToInt(Integer::parseInt): 스트림의 각 문자열 요소를 정수로 변환(중간 연산) +- .boxed(): IntStream을 Stream로 변환(중간 연산) +- .collect(Collectors.toList()): 스트림의 정수들을 리스트로 모음(최종 연산) +--- +### static +멤버 변수 혹은 메서드를 static으로 선언 -> 객체 생성하지 않고 사용 가능 +- static 키워드는 final 키어드와 함께 상수 정의함 +- 일반적으로 생성자에는 사용X + - 특정 객체의 상태를 초기화하는 생성자와는 역할 맞지 않음 +- 새로 추가된 멤버 변수를 반환하는 메서드 -> static 메서드 되면 안됨 + - static 메소드는 특정 객체의 상태에 접근할 수 없음 + - 객체마다 다른 값을 가질 수 있는 멤버 변수 반환하는 getter메소드는 static으로 만들면 안됨 + - 입력 값에 의존하여 항상 같은 결과를 반환하는 유틸리티 메소드 등에 적합 +### final +- 한 번 값을 정의하면 더 이상 다른 값 넣을 수 없음 +- 보통 상수는 객체의 생성과 무관 -> static final From 30ac3356d23cc1330c8ec937acc9085cb97a90ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=9C=A4=EC=A3=BC?= Date: Sat, 5 Apr 2025 23:14:35 +0900 Subject: [PATCH 2/7] =?UTF-8?q?[2=EC=A3=BC=EC=B0=A8]=20{=EC=A0=95=EC=9C=A4?= =?UTF-8?q?=EC=A3=BC}=20=ED=95=99=EC=8A=B5=20PR=20=EC=A0=9C=EC=B6=9C?= =?UTF-8?q?=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...{\354\240\225\354\234\244\354\243\274}.md" | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 "week2/[2\354\243\274\354\260\250] {\354\240\225\354\234\244\354\243\274}.md" diff --git "a/week2/[2\354\243\274\354\260\250] {\354\240\225\354\234\244\354\243\274}.md" "b/week2/[2\354\243\274\354\260\250] {\354\240\225\354\234\244\354\243\274}.md" new file mode 100644 index 0000000..52ba742 --- /dev/null +++ "b/week2/[2\354\243\274\354\260\250] {\354\240\225\354\234\244\354\243\274}.md" @@ -0,0 +1,25 @@ +# 2주차 학습 내용 + +### 코드 변경점 +- 중복되는 입력값 검증은 InputValidator 에서 처리 + - 특정 객체 상태와 무관하게 공통적으로 사용하는 검증 로직이라고 생각해서 static 메서드로 선언했습니다 +- 에러 메시지 enum 으로 관리 +- LottoConstants 에서 상수 관리 +- BonusNumber, PurchaseAmount 생성자에서 바로 유효성 검사 및 파싱 +- BonusNumber 에서 WinningNumbers 객체 받아 보너스 번호 받아 중복 여부 검증 +- Manager 이름 수정 +- 통제할 수 없는 랜덤 번호 생성 인터페이스로 분리 + - 랜덤 번호 생성일 경우 우리가 통제할 수 없어 테스트하기 어려움 + - 따라서 통제할 수 없는 구조를 통제 가능한 구조로 만들기 위함(?) + - static 메서드로 선언할 경우 프로그램의 시작과 동시에 static 영역에 할당되어 + 프로그램 끝날때 까지 유지되기 때문에 테스트 결과를 고정시킬 수 없음(?) + +### 고민했던 점 +책임을 분리하려고 고민을 많이 했는데 어디부터 어디까지의 책임을 하나의 객체의 책임이라고 하는지의 기준을 +아직은 잘 모르겠습니다. (예를들면 보너스 번호를 파싱하고 검증하는 책임을 생성자가 다 해야하는지 +아니면 검증은 검증하는 객체에서 따로 해야하는지) +--- + +의존성과 관련된 부분은 아직 잘 모르겠습니다. 의존성 주입에 대해서 알아보긴 했지만, 아직 어떻게 사용해야하는지 +왜 그렇게 해야하는지에 대해서는 이해를 못한 상태에서 코드에 적용하다 보니 사실 어떤 방식으로 의존성이 낮아지는지는 +잘 모르겠습니다.. \ No newline at end of file From f4377a78d137b05de77901ec2b3e55842bb17079 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=9C=A4=EC=A3=BC?= Date: Sat, 5 Apr 2025 23:24:57 +0900 Subject: [PATCH 3/7] =?UTF-8?q?[2=EC=A3=BC=EC=B0=A8]=20{=EC=A0=95=EC=9C=A4?= =?UTF-8?q?=EC=A3=BC}=20=ED=95=99=EC=8A=B5=20PR=20=EC=A0=9C=EC=B6=9C?= =?UTF-8?q?=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...74\354\260\250] {\354\240\225\354\234\244\354\243\274}.md" | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git "a/week2/[2\354\243\274\354\260\250] {\354\240\225\354\234\244\354\243\274}.md" "b/week2/[2\354\243\274\354\260\250] {\354\240\225\354\234\244\354\243\274}.md" index 52ba742..285e36c 100644 --- "a/week2/[2\354\243\274\354\260\250] {\354\240\225\354\234\244\354\243\274}.md" +++ "b/week2/[2\354\243\274\354\260\250] {\354\240\225\354\234\244\354\243\274}.md" @@ -15,10 +15,12 @@ 프로그램 끝날때 까지 유지되기 때문에 테스트 결과를 고정시킬 수 없음(?) ### 고민했던 점 + 책임을 분리하려고 고민을 많이 했는데 어디부터 어디까지의 책임을 하나의 객체의 책임이라고 하는지의 기준을 아직은 잘 모르겠습니다. (예를들면 보너스 번호를 파싱하고 검증하는 책임을 생성자가 다 해야하는지 아니면 검증은 검증하는 객체에서 따로 해야하는지) ---- + + 의존성과 관련된 부분은 아직 잘 모르겠습니다. 의존성 주입에 대해서 알아보긴 했지만, 아직 어떻게 사용해야하는지 왜 그렇게 해야하는지에 대해서는 이해를 못한 상태에서 코드에 적용하다 보니 사실 어떤 방식으로 의존성이 낮아지는지는 From 4da69b06cba8c212b8d3abe09cf9b003d120659f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=9C=A4=EC=A3=BC?= Date: Wed, 30 Apr 2025 18:34:35 +0900 Subject: [PATCH 4/7] =?UTF-8?q?[3=EC=A3=BC=EC=B0=A8]=20{=EC=A0=95=EC=9C=A4?= =?UTF-8?q?=EC=A3=BC}=20=ED=95=99=EC=8A=B5=20PR=20=EC=A0=9C=EC=B6=9C?= =?UTF-8?q?=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...{\354\240\225\354\234\244\354\243\274}.md" | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 "week3/[3\354\243\274\354\260\250] {\354\240\225\354\234\244\354\243\274}.md" diff --git "a/week3/[3\354\243\274\354\260\250] {\354\240\225\354\234\244\354\243\274}.md" "b/week3/[3\354\243\274\354\260\250] {\354\240\225\354\234\244\354\243\274}.md" new file mode 100644 index 0000000..f6eb93d --- /dev/null +++ "b/week3/[3\354\243\274\354\260\250] {\354\240\225\354\234\244\354\243\274}.md" @@ -0,0 +1,95 @@ +# 3주차 학습 내용 + +— + +## **1. 의존성 주입 종류 알아오기** +> ### 의존성 주입 +> - 클래스 간 강한 결합을 풀어주어 조금 더 OOP 스러운 개발을 할 수 있도록 만들어 줌. +> - 필요한 객체를 직접 생성하는 것이 아닌 외부로부터 객체를 받아 사용하는 것이다. +> - 의존성 주입을 해야하는 이유 +> - test가 용이해진다 +> - 코드의 재사용성을 높여준다 +> - 객체 간의 의존성(종속성)을 줄이거나 없앨 수 있다 +> - 객체 간의 결합도를 낮추면서 유연한 코드를 작성할 수 있다 +### 1. 필드 주입 +- Service를 사용하려는 Controller의 필드에 직접 @Autowired를 부여하는 방식 +- 특징 + - 코드가 간결해진다 + - 단, 외부에서 변경이 불가능하여 테스트하기 어려운 단점이 있다 + - DI 프레임워크가 없으면 아무것도 할 수 없다 + - 애플리케이션의 실제 코드와 상관없는 특정 테스트를 하고 싶을 때 사용한다 + - 만약, 의존관계를 필수적으로 넣지 않으려면 @Autowired(required=false) 옵션 처리를 통해 필수가 아님을 명시할 수 있다. +### 2. 수정자 주입 +- Setter 메서드를 통해 주입하는 방식 +- @Component를 통해 실행하는 클래스를 스프링 빈으로 등록하고 의존관계를 주입하게 된다. +- @Autowired가 있는 수정자들을 자동으로 의존관계를 주입한다. +- 특징 + - 선택과 변경 가능성이 있는 의존 관계에 사용 + - 자바 빈 프로퍼티 규약의 수정자 메서드 방식을 사용하는 방법이다 + - set필드명 메서드를 생성하여 의존 관계를 주입한다 + - @Autowired를 입력하지 않으면 실행이 되지 않는다 +### 3. 생성자 주입 +- 생성자를 통해서 의존 관계를 주입하는 방법 +- 생성자에 @Autowired를 하면 스프링 컨테이너에 @Component로 등록된 빈에서 생성자에게 필요한 빈들을 주입한다. +- 특징 + - 생성자 호출 시점에 1번만 호출되는 것을 보장한다 + - 불변과 필수 의존 관계에 사용한다 + - 생성자가 1개만 존재하는 경우 @Autowired를 생략해도 자동 주입된다. + - NPE(NullPointerException)를 방지할 수 있다. + - 주입받을 필드를 final로 선언 가능하다 + +## **2. 스프링 사용 이유 공부** +### 1. 모듈성과 유연성 +Spring은 다양한 모듈로 구성되어 있어 필요한 기능만 선택적으로 사용할 수 있다. 이는 개발자에게 +모듈성과 유연성을 제공하며, 필요한 부분만 사용하여 불필요한 복잡성을 피할 수 있다. + +### 2. IoC(Inversion of Control) 컨테이너 +Spring의 IoC 컨테이너는 객체의 생성, 관리, 의존성 주입을 처리하여 코드의 유지보수성과 테스트 용이성 +을 향상시킨다. 개발자는 객체의 라이프사이클을 직접 관리하지 않아도 되므로 개발 생산성이 향상된다. + +### 3. AOP(Aspect-Oriented Programming) +Spring은 AOP를 지원하여 핵심 비즈니스 로직과는 별도로 관심사를 모듈화 할 수 있다. 예를 들어, +로깅, 트랜잭션 관리, 보안 등과 같은 공통된 관심사를 별도의 모듈로 분리하여 코드의 중복을 방지하고 +유지 보수성을 향상시킨다. + +### 4. 데이터 액세스 및 트랜잭션 관리 +Spring은 데이터베이스 액세스를 위한 강력한 기능을 제공한다. JDBC와 ORM 프레임워크를 통한 데이터 액세스, +트랙잭션 관리를 간편하게 구현할 수 있다. + +### 5. MVC 웹 프레임워크 +Spring MVC는 모델-뷰-컨트롤러 아키텍처를 제공하여 웹 애플리케이션을 구조화하고 유지보수하기 쉽게 +한다. 또한, 다양한 확장 가능한 기능과 통합이 가능한 구조를 제공한다. + +### 6. 보안 +Spring Security는 보안 관련 기능을 제공하여 사용자 인증, 권한 부여, 보안 설정을 쉽게 처리할 수 있다. + +### 7. 테스트 용이성 +Spring은 의존성 주입과 컨텍스트 분리로 테스트하기 쉬운 코드를 작성할 수 있도록 지원하여, 테스트 +주도 개발(TDD)에 유리한 환경을 제공한다. + +### 8. 커뮤니티와 생태계 +Spring은 큰 커뮤니티와 다양한 생태계를 가지고 있어서 문제 해결, 정보 공유, 다양한 라이브러리 및 플러그인을 +활용할 수 있다. + +## **3. 스프링 컨테이너 알아오기** +### 스프링 컨테이너 +- 스프링에서는 자바 객체를 빈(Bean)이라 한다 +- 스프링 컨테이너는 내부에 존재하는 빈의 생명 주기를 관리(빈의 생성, 관리, 제거 등)하며, 생성된 +빈에게 추가적인 기능을 제공하는 것이다. + +### 스프링 컨테이너 종류 +스프링 컨테이너는 Beanfactory와 ApplicationContext 두 종류의 인터페이스로 구현되어 있다. 빈 팩토리는 빈의 생성과 +관계설정 같은 제어를 담당하는 IoC 오브젝트이고, 빈 팩토리를 좀 더 확장한 것이 애플리케이션 컨텍스트이다. + +**1.BeanFactory** +- 스프링 컨테이너의 최상위 인터페이스 +- BeanFactory는 빈을 등록, 생성, 조회 등의 빈을 관리하는 역할을 하며, getBean() 메서드를 통해 빈을 인스턴스화 할 수 있다. + +**2.ApplicationContext** +- 애플리케이션 컨텍스트는 BeanFactory의 기능을 상속받아 제공한다. +- 빈을 관리하고 검색하는 기능을 BeanFactory가 제공하고, 그 외의 부가 기능을 제공한다. +- 부가 기능 + - MessageSource: 메시지 다국화를 위한 인터페이스 + - EnvironmentCapable: 개발, 운영, 환경변수 등으로 나누어 처리하고, 애플리케이션 구동 시 필요한 정보들을 관리하기 위한 인터페이스 + - ApplicationEventPublisher: 이벤트 관련 기능을 제공하는 인터페이스 + - ResourceLoader: 파일, 클래스 패스, 외부 등 리소스를 편리하게 조회 \ No newline at end of file From d1566fc116a36478039a921d8c93fd6ba9d4d1cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=9C=A4=EC=A3=BC?= Date: Wed, 7 May 2025 21:16:37 +0900 Subject: [PATCH 5/7] =?UTF-8?q?[6=EC=A3=BC=EC=B0=A8]=20{=EC=A0=95=EC=9C=A4?= =?UTF-8?q?=EC=A3=BC}=20=ED=95=99=EC=8A=B5=20PR=20=EC=A0=9C=EC=B6=9C?= =?UTF-8?q?=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0]\354\240\225\354\234\244\354\243\274.md" | 304 ++++++++++++++++++ 1 file changed, 304 insertions(+) create mode 100644 "week6/[6\354\243\274\354\260\250]\354\240\225\354\234\244\354\243\274.md" diff --git "a/week6/[6\354\243\274\354\260\250]\354\240\225\354\234\244\354\243\274.md" "b/week6/[6\354\243\274\354\260\250]\354\240\225\354\234\244\354\243\274.md" new file mode 100644 index 0000000..335fc03 --- /dev/null +++ "b/week6/[6\354\243\274\354\260\250]\354\240\225\354\234\244\354\243\274.md" @@ -0,0 +1,304 @@ +# 6주차 학습 내용 + +### Spring MVC Lifecycle +> Filter -> DispatcherServlet -> HandlerMapping ->HandlerInterceptor -> controller -> Service -> +> Repository -> ViewResolver 순으로 들어오게 됨 + +**1. Filter** +- Web Application의 전역적인 로직을 담당한다. +- Filter라는 단어 뜻에서 알 수 있듯이, 전체적인 필터링(설정)을 하는 곳 +- DispatcherServlet에 들어가기 전인 Web Application단에서 실행됨 + +**2. Dispatcher Servlet** +- 들어오는 모든 Request를 우선적으로 받아 처리해주는 서블릿이다. +- HandlerMapping에게 Request에 대해 매필할 Controller 검색을 요청한다. +- HandlerMapping으로부터 Controller 정보를 반환받아 해당 Controller와 매핑시킨다. +- Dispatcher라는 단어가 '배치 담당자'라는 뜻이 있듯이, 말 그대로 Request에 대해 어느 컨트롤러로 매핑시킬것인지 배치하는 역할을 함. + +**3. HandlerMapping** +- DispatcherServlet으로부터 검색을 요청받은 Controller를 찾아 정보를 리턴해준다. + +**4. HandlerInterceptor** +- Request가 Controller에 매핑되기전 앞단에서 부가적인 로직을 추가할 수 있다. +- 주로 세션, 쿠키, 권한 인증 로직에서 많이 사용됨. + +**5. Controller** +- Request와 매칭되는 곳 +- Request에 대해 어떤 로직(Service)으로 처리할 것인지를 결정하고, 그에 맞는 Service를 호출한다. +- Service Bean을 스프링 컨테이너로부터 주입받아야 한다. Service Bean의 메소드를 호출해야 하기 때문 + +**6. Service** +- 데이터 처리 및 가공을 위한 비즈니스 로직을 수행한다. +- Request에 대한 실질적인 로직을 수행하기 때문에, Spring MVC Request Lifecycle의 심장이라고 불 수 있다. +Service가 없다면 서버 애플리케이션의 존재 이유도 없다. +- Repository를 통해 DB에 접근하여 데이터 CRUD(Create, Read, Update, Delete)를 처리한다. + +**7. Repository(DAO)** +- DB에 접근하는 객체. DAO(Data Access Object)라고 부름. +- Service에서 DB에 접근할 수 있게 하여 데이터의 CRUD를 할 수 있게 해준다. + +**8. ViewResolver** +- Controller에서 리턴한 View의 이름을 DispatcherServlet으로부터 넘겨받고, 해당 View를 렌더링함. +- 렌더링한 View 는 DispatcherServlet으로 리턴하고, DispatcherServlet에서는 해당 View 화면을 Response 함. +### Dispatcher servlet의 역할 +>디스패처 서블릿의 dispatch는 "보내다"라는 뜻으로 디스패처 서블릿은 **HTTP 프로토콜로 들어오는 모든 요청을 가장 먼저 받아 +적합한 컨트롤러에 위임해주는 프론트 컨트롤러(Front Controller)** 라고 정의할 수 있다. +1. 클라이언트로부터 어떠한 요청이 오면 Tomcat(톰캣)과 같은 서블릿 컨테이너가 요청을 받게 됨 +2. 이 모든 요청을 프론트 컨트롤러인 디스패처 서블릿이 가장 먼저 받게 됨 +3. 디스패처 서블릿은 공통적인 작업을 먼저 처리한 후에 해당 요청을 처리해야 하는 컨트롤러를 찾아 작업을 위임함 + +**Front Controller(프론트 컨트롤러)** +주로 서블릿 컨테이너의 제일 앞에서 서버로 들어오는 클라이언트의 모든 요청을 받아 처리해주는 컨트롤러 +로써, MVC 구조에서 함께 사용되는 디자인 패턴임 + +### Bean이란? +스프링 컨테이너에 의해 관리되는 재사용 가능한 소프트웨어 컴포넌트임. +=> 스프링 컨테이너가 관리하는 자바 객체를 뜻하며, 하나 이상의 빈(Bean)을 관리한다. +- 빈은 인스턴스화된 객체를 의미하며, 스프링 컨테이너에 등록된 객체를 스프링 빈이라고 한다. +- 스프링 간 객체가 의존관계를 관리하도록 하는 것에 가장 큰 목적이 있다. +- 객체가 의존관계를 등록할 때 스프링 컨테이너에서 해당하는 빈을 찾고, 그 빈과 의존성을 만든다. +### Bean Lifecycle +해당 객체가 언제 어떻게 생성되어 소멸되기 전까지 어떤 작업을 수행하고 언제, 어떻게 소멸되는지 +일련의 과정을 이르는 말이다. +- Spring의 Bean은 Java 또는 XML bean 정의를 기반으로 IoC 컨테이너가 시작될 때 인스턴스화 되어야 한다. +- Bean을 사용 가능한 상태로 만들기 위해 사전, 사후 초기화 단계를 수행해야 할 수 있다. +- 그 후 Bean이 더 이상 필요하지 않으면 IoC Container에서 제거된다. +- 다른 시스템 리소스를 해제하기 위해 사전 및 사후 소면 단계를 수행해야 할 수 있다. + +**Bean Life Cycle 흐름** +1. 스프링 컨테이너 생성 +2. 스프링 빈 생성 +3. 의존성 주입 +4. 초기화 콜백: 빈이 생성되고, 빈의 의존관계 주입이 완료된 후 호출 +5. 사용 +6. 소멸전 콜백: 빈이 소멸되기 직전에 호출 +7. 스프링 종료 +### Spring 어노테이션 10가지와 그에 대한 설명 +**1. ```@Component```** +- 역할: 스프링이 자동으로 객체(bean)로 등록하게 해주는 어노테이션 +- ```@Component```는 ```@Bean```과 다르게 name이 아닌 value를 이용해 Bean의 이름을 지정한다 +- 사용 위치: 클래스 + +**2. ```@Controller```** +- 역할: 웹 요청을 처리하는 컨트롤러 클래스에 사용 +- ```@Component``` 포함됨(자동 등록) + +**3. ```@RestController```** +- 역할: ```@Controller``` + ```@ResponseBody``` 기능 +- REST API용 컨트롤러에 줄 사용 + +**4. ```@Service```** +- 역할: 비즈니스 로직을 처리하는 서비스 클래스에 사용 + +**5. ```@Repository```** +- 역할: 데이터 접근 계층(DAO)에 사용. 데이터베이스 예외를 Spring 예외로 변환 + +**6. ```@Autowired```** +- 역할: 의존성 주입(DI)을 자동으로 해줌 + +**7. ```@Configuration```** +- 역할: Java 기반 설정 파일로 사용됨. 설정 클래스임을 명시 + +**8. ```@Bean```** +- 역할: 개발자가 직접 객체를 생성해서 스프링 빈으로 등록할 때 사용 + +**9. ```@RequestMapping```** +- 역할: URL 경로와 컨트롤러 메서드를 매핑 +- 변형: ```@GetMapping```, ```@PostMapping```, ```@PutMapping```, ```@DeleteMapping``` + +**10. ```@Value```** +- 역할: ```@application.properties```또는 환경변수의 값을 주입받을 때 사용 + +### Spring 의존성 주입 방식 +**1. 생성자 주입** +- 생성자를 통해 의존 객체를 주입받는 방식 +- 불변성, 테스트 용이성이 가장 뛰어남 +- 주입 대상이 ```final``` 필드로 설정될 수 있어 안정적 +```java +@Component +public class OrderService { + + private final MemberRepository memberRepository; + + @Autowired + public OrderService(MemberRepository memberRepository) { + this.memberRepository = memberRepository; + } +} +``` +필수 의존성은 생성자 주입을 쓰는 것이 가장 좋음 + +**2. 세터 주입** +- 세터 메서드를 통해 의존 객체를 주입받는 방식 +- 선택적 의존성에 적합(필수 아님) +```java +@Component +public class OrderService { + + private MemberRepository memberRepository; + + @Autowired + public void setMemberRepository(MemberRepository memberRepository) { + this.memberRepository = memberRepository; + } +} +``` +주입을 뺴먹으면 런타임 오류 발생 가능 + +**3. 필드 주입** +- 필드에 직접 ```@Autowired```를 붙이는 방식 +- 가장 간단하지만, 테스트 및 유지보수 어려움 +```java +@Component +public class OrderService { + + @Autowired + private MemberRepository memberRepository; +} +``` +### 생성자 주입 방식(중요) +> - 객체 생성 시 생성자를 통해 의존성을 주입하는 방식 +> - 객체가 무조건 필요한 의존성을 명시적으로 요구함 + +**특징** +- 의존성이 없으면 객체 생성 자체가 불가능 -> 불변성 보장 +- 명시적이고 테스트하기 쉬웁 +- 스프링에서는 생성자에 @Autowired 생략 가능 (단일 생성자일 경우) +```java +@Component +public class Car { + private final Engine engine; + + // 생성자 주입 + public Car(Engine engine) { + this.engine = engine; + } + + public void start() { + engine.run(); + } +} +``` +```java +@Component +public class Engine { + public void run() { + System.out.println("Engine running"); + } +} +``` + +**장점** +- 불변성 보장(final 필드 사용 가능) +- 순환 참조 빨리 발견 가능 +- 단위 테스트 용이 (생성자에 mock 전달) + +**단점** +- 의존성 많을 경우 생성자 길어짐 +- 필수 의존성만 주입 가능(선택적 주입 어려움) +## DB + +### 관계형 데이터배이스란? +데이터를 테이블 형태로 저장한다. 각 데이터 항목들은 행(row)에 저장되고, 항목의 속성은 열(column)이라고 +표현한다. 열은 항목의 속성인 만큼 입력되는 데이터의 유형이 정해진다. +- 열(column): 필드(field) 라고도 부르며, 항목의 속성(명칭)을 나타낸다. 필드 마다 각각 정수, 텍스트 +같은 데이터 유형을 정할 수 있다. +- 행(row): 레코드(record) 라고도 부르며, 각 데이터 항목을 저장한다. +- 스키마(schema): 필드는 데이터 유형뿐만 아니라 제약사항도 지정할 수 있는데 이러한 제약사항을 스키마라고 부른다. +예를들어 필드는 중복 값을 해당 행에 저장할 수 없다거나, 반드시 값을 가져야 한다(not null)는 노건 등을 걸 수 있다. + +### 관계형 데이터베이스에서 Relationship이란? +둘 이상의 테이블 간의 논리적인 연관성 또는 연결을 의미한다. 현실 세계의 객체(예: 학생, 수강신청 등)들 간의 관계를 +테이블 간의 구조로 표현하는 방식이다. +- 현실 세계는 독립된 개체들 간의 관계로 구성되어 있음 (예: 학생은 여러 과목을 수강함) +- 중복을 줄이고 정규화된 구조를 만들기 위해 여러 테이블로 분리하게 되며, 이때 각 테이블은 관계를 통해 연결됨 +- 관계 설정은 외래 키(FK)를 통해 이루어짐 + +**관계의 종류** +**1. 1:1 (일대일 관계)** +- 한 테이블의 한 레코드는 다른 테이블의 한 레코드와만 연결됨 +- 예: 사람 <-> 주민등록증 + +**2. 1:N (일대다 관계)** +- 한 테이블의 한 레코드는 다른 테이블의 여러 레코드와 연결될 수 있음 +- 예: 부서 <-> 직원 (한 부서에는 여러 직원이 속할 수 있음) + +**3. M:N (다대다 관계)** +- 한 테이블의 여러 레코드는 다른 테이블의 여러 레코드와 연결될 수 있음 +- 예: 학생 <-> 강좌 + - 한 학생은 여러 강좌를 수강할 수 있고, 하나의 강좌는 여러 학생이 있을 수 있음 + - 일반적으로 중간 테이블(연결 테이블)을 사용해 1:N 관계로 분해함 + - 예: 수강신청 테이블이 중간에서 연결 역할 수행 + +### PK와 FK +**1. PK(Primary Key: 기본키)** +테이블에서 각 행(row)을 유일하게 식별하기 위한 컬럼(또는 컬럼들의 조합) +- 중복 불가: 하나의 테이블에 같은 PK 값을 가진 행이 두 개 이상 존재할 수 없음 +- NULL 불가: PK는 반드시 값이 있어야 하며, NULL을 허용하지 않음 +- 한 테이블에 1개만 존재 가능(하지만 컬럼 여러 개로 구성된 복합 키는 가능함) +```sql +CREATE TABLE Students ( + student_id INT PRIMARY KEY, + name VARCHAR(100), + age INT +); +``` +여기서 ```student_id```가 PK. 모든 학생은 고유한 ID를 가져야 하며, 같은 ID가 중복되면 안됨. + +**2. FK(Foreign Key: 외래키)** +다른 테이블의 Primary Key를 참조하는 컬럼. 두 테이블 사이의 관계(연결고리)를 정의할 때 사용 +- 외래 키는 다른 테이블의 PK 또는 유니크 키를 참조해야 함 +- 참조 무결성을 유지: 존재하지 않는 PK 값을 참조할 수 없음 +- 기본적으로 NULL 허용 가능(제약조건에 따라 다름) +```sql +CREATE TABLE Enrollments ( + enrollment_id INT PRIMARY KEY, + student_id INT, + course_name VARCHAR(100), + FOREIGN KEY (student_id) REFERENCES Students(student_id) +); +``` +여기서 ```Enrollments``` 테이블의 ```student_id```는 ```Students``` 테이블의 ```student_id```를 참조하는 외래키. +수강 정보를 저장할 때 반드시 존재하는 학생 ID여야 함 +### 테이블 제약 조건 +- 테이블에 부적절한 자료가 입력되는 것을 방지하기 위해서 여러가지 규칙을 적용해 놓는 것 +- 테이블 안에서 데이터의 성격을 정의하는 것 + +**1. NOT NULL 조건** +- 해당 칼럼에 반드시 값이 기록 되어야 하는 경우에 사용 +- NOT NULL 제약조건 설정 시 해당 컬럼에는 꼭 데이터를 입력헤야 함 + +**2. UNIQUE 조건** +- 데이터의 유일성을 보장(=> 중복되는 데이터가 존재할 수 없음)하고, 자동으로 인덱스가 생성 +- unique은 null 허용하지만, primary key는 null 허용 안함 +- unique은 하나의 테이블에 여러개 올 수 있지만, primary key는 하나만 존재 + +**3. CHECK 조건** +- 컬럼의 값을 어떤 특정 범위로 제한 +- 비교 값은 리터럴만 사용 가능(변하는 값이나 함수 사용 X) + +**4. DEFAULT (컬럼 기본값) 지정** +- 데이터를 입력하지 않아도 지정된 값이 기본으로 입력된다. +- default라고 값을 명시하면 기본값이 들어감 +- 열이름이 명시되지 않으면 자동 기본값 +- 값이 직접 명기되면 기본값은 무시됨 + +**5. PRIMARY KEY 지정** +- 기본키 : UNIQUE + NOT NULL 의 결합과 같음 +- 기본키는 그 데이터 행을 대표하는 컬럼으로서의 역할을 수행하여 다른 테이블에서 외래키들이 참조할 수 있는 +키로서의 자격을 가지고 있다. => 참조 무결성 +- 한 테이블당 한 개만 설정할 수 있음 +- 기본키는 컬럼 1개로 설정할 수도 있고, 여러 개의 칼럼을 묶어서 복합키로 설정할 수 있음 +- UNIQUE 제약조건과 마찬가지로 기본키를 정의하면 자동으로 인덱스를 생성, 그 이름은 기본 키 제약조건의 이름과 같다. +> **INDEX** +> 검색 키로서 검색 속도를 향상시킨다. (UNIQUE, PRIMARY KEY 생성시 자동적으로 생긴다.) + +**6. FOREIGN KEY(외래키) 지정** +- 기본키를 참조하는 컬럼 or 컬럼들의 집합 (외래키는 기본키나 유니크가 아니면 생성 제약) +- 외래키를 가지는 컬럼의 데이터 형은 외래키가 참조하는 기본키의 컬럼과 데이터 형이 일치해야 함 + (이를 어기면 참조 무결성 제약에 의해 테이블 생성할 수 없음) +- 외래키에 의해 참조되고 있는 기본키: 삭제 불가 +- 제공되는 값 외에는 NULL을 사용할 수 있음 +- on update cascade 하면 기본키가 수정될 경우 외래키도 같이 수정해준다는 말 +- ON DELETE CASCADE 연산자와 함께 정의된 외래키의 데이터는 그 기본키가 삭제될 때 같이 삭제 됨 + From dcfc093651029183f88af4dcbb46bfe8b363e06b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=9C=A4=EC=A3=BC?= Date: Sun, 11 May 2025 17:49:22 +0900 Subject: [PATCH 6/7] =?UTF-8?q?[7=EC=A3=BC=EC=B0=A8]=20{=EC=A0=95=EC=9C=A4?= =?UTF-8?q?=EC=A3=BC}=20=ED=95=99=EC=8A=B5=20PR=20=EC=A0=9C=EC=B6=9C?= =?UTF-8?q?=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0]\354\240\225\354\234\244\354\243\274.md" | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 "week7/[7\354\243\274\354\260\250]\354\240\225\354\234\244\354\243\274.md" diff --git "a/week7/[7\354\243\274\354\260\250]\354\240\225\354\234\244\354\243\274.md" "b/week7/[7\354\243\274\354\260\250]\354\240\225\354\234\244\354\243\274.md" new file mode 100644 index 0000000..244de0c --- /dev/null +++ "b/week7/[7\354\243\274\354\260\250]\354\240\225\354\234\244\354\243\274.md" @@ -0,0 +1,74 @@ +# 7주차 학습 내용 + +## JPA 개념 +> JPA(Java Persistence API)는 자바 객체를 데이터베이스 테이블과 매핑하고, 데이터를 저장 +> 저장·조회·수정·삭제(CRUD)할 수 있도록 도와주는 자바 표준 ORM API + +SQL을 직접 작성하지 않고도 자바 객체 중심으로 DB 작업이 가능하며, 유지보수성과 생산성이 크게 향상됨 + +### JPA와 JDBC의 차이란? +| 항목 | JDBC | JPA | +|------------|---------------------------|----------------------------------------| +| 쿼리 작성 | 직접 SQL 작성 | JPQL 또는 메서드로 처리 | +| 코드량 | 많음 | 적음 (간결한 코드) | +| 객체-DB 매핑 | 수동 | 자동 | +| 유지보수 | 어려움 | 쉬움 | +| 트랜잭션 | 직접 처리 | @Transactional 등으로 간편 처리 가능 | + +### ORM이란 무엇이고 JPA가 ORM 프레임워크에서 어떤 역할을 하는지 +- **ORM(Object-Relational Mapping)** 은 객체(Object)와 관계형 데이터(Relational)를 +매핑(Mapping)하는 기법 +- 자바 객체와 DB 테이블을 1:1로 연결해 자동으로 SQL이 생성되도록 함 +- JPA는 ORM을 구현한 자바 표준 인터페이스 + + +## 엔티티 +조직이 관리하고자 하는 데이터와 관련된 사용자 환경 속의 사람, 장소, 객체, 이벤트 또는 개념 +### 엔티티 필수 어노테이션의 종류는? +1. ```@Entity``` +해당 클래스가 JPA에서 관리할 엔티티임을 지정 +2. ```@Id``` +기본 키를 지정 +3. ```@GeneratedValue``` +기본 키 자동 생성 방식 지정 +4. ```@Column``` +필드와 DB 컬럼을 매핑 (생략 가능) +### JPA에서 엔티티의 필수 조건은? +1. ```@Entity``` 어노테이션 필수 +2. 기본 키 필수(```@Id```) +3. 기본 생성자(public 또는 protected)가 있어야 함 +4. final 클래스, enum, 인터페이스는 안 됨 +5. 필드는 private으로 선언하고 getter/setter 사용 권장 +### 엔티티의 생명주기란? +1. 비영속(new/transient): 영속성 컨텍스트와 전혀 관계가 없는 상태 +2. 영속(persistent): 영속성 컨텍스트에 저장된 상태 +3. 준영속(detached): 영속성 컨텍스트에 저장되었다가 분리된 상태 +4. 삭제(removed): 삭제된 상태 +## 영속성 컨텍스트 +- 엔티티를 영구 저장하는 환경 +- 동일 트랜잭션 내에서 같은 객체를 반환하거나, 변경 감지, 쓰기 지연, 1차 캐시 등을 통해 +성능 향상과 일관성 유지가 가능함 +### 영속성 컨텍스트 상태의 종류는? +**1. 비영속 (New / Transient)** +엔티티 객체가 생성되었지만, 아직 영속성 컨텍스트와 연결되지 않은 상태. +이 상태의 엔티티는 영속성 컨텍스트의 어떤 관리도 받지 않으며, 데이터베이스에도 저장되지 않음 +**2. 영속 (Managed / Persistent)** +엔티티 객체가 영속성 컨텍스트에 저장되어 관리되는 상태. +영속 상태의 엔티티는 영속성 컨텍스트의 1차 캐시에 저장되고, 변경 감지 기능을 통해 +트랜잭션 커밋 시점에 데이터베이스에 반영될 수 있음 +**3. 분리 (Detached)** +이전에 영속성 컨텍스트에 의해 관리되던 엔티티 객체가 더 이상 영속성 컨텍스트의 관리를 받지 않는 상태. +영속성 컨텍스트가 종료되거나, detach() 메서드를 호출하여 엔티티를 분리하면 이 상태가 됨. +분리된 엔티티는 영속성 컨텍스트의 변경 감지 기능을 활용할 수 없음. +**4.삭제 (Removed)** +엔티티 객체가 영속성 컨텍스트에서 삭제 대상으로 지정된 상태. +remove() 메서드를 호출하면 엔티티는 삭제 상태가 되며, 트랜잭션 커밋 시점에 데이터베이스에서 해당 엔티티가 삭제됨. + +## 연간관계 +### 연간관계의 종류와 JPA에서의 표현법은? +| 관계 유형 | 설명 | JPA 표현법 | +|-----------|--------------------------------|----------------| +| 1:1 | 하나의 객체가 하나의 객체를 참조 | `@OneToOne` | +| 1:N | 하나의 객체가 여러 객체를 참조 | `@OneToMany` | +| N:1 | 여러 객체가 하나의 객체를 참조 | `@ManyToOne` | +| N:N | 다수 객체가 서로를 참조 | `@ManyToMany` | \ No newline at end of file From d98435b061f6ca901bb69f1945b98b5542f454fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=9C=A4=EC=A3=BC?= Date: Sun, 18 May 2025 14:12:39 +0900 Subject: [PATCH 7/7] =?UTF-8?q?[8=EC=A3=BC=EC=B0=A8]=20{=EC=A0=95=EC=9C=A4?= =?UTF-8?q?=EC=A3=BC}=20=ED=95=99=EC=8A=B5=20PR=20=EC=A0=9C=EC=B6=9C?= =?UTF-8?q?=ED=95=A9=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0]\354\240\225\354\234\244\354\243\274.md" | 63 ++++++++++++++++++ .../comment/dto/CommentController.java | 41 ++++++++++++ .../comment/dto/request/CommentRequest.java | 11 ++++ .../comment/dto/response/CommentResponse.java | 13 ++++ .../dbswncrudapi/comment/entity/Comment.java | 40 +++++++++++ .../comment/repository/CommentRepository.java | 12 ++++ .../comment/service/CommentService.java | 56 ++++++++++++++++ .../member/controller/MemberController.java | 37 +++++++++++ .../controller/dto/request/MemberRequest.java | 20 ++++++ .../dto/response/MemberResponse.java | 27 ++++++++ .../dbswncrudapi/member/entity/Member.java | 43 ++++++++++++ .../repository/JpaMemberRepository.java | 11 ++++ .../member/service/MemberService.java | 41 ++++++++++++ .../post/controller/PostController.java | 48 ++++++++++++++ .../dto/request/CreatePostRequest.java | 20 ++++++ .../dto/request/UpdatePostRequest.java | 9 +++ .../controller/dto/response/PostResponse.java | 30 +++++++++ .../dbswncrudapi/post/entity/Post.java | 38 +++++++++++ .../post/repository/PostRepository.java | 14 ++++ .../post/service/PostService.java | 66 +++++++++++++++++++ 20 files changed, 640 insertions(+) create mode 100644 "week8/[8\354\243\274\354\260\250]\354\240\225\354\234\244\354\243\274.md" create mode 100644 week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/comment/dto/CommentController.java create mode 100644 week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/comment/dto/request/CommentRequest.java create mode 100644 week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/comment/dto/response/CommentResponse.java create mode 100644 week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/comment/entity/Comment.java create mode 100644 week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/comment/repository/CommentRepository.java create mode 100644 week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/comment/service/CommentService.java create mode 100644 week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/member/controller/MemberController.java create mode 100644 week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/member/controller/dto/request/MemberRequest.java create mode 100644 week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/member/controller/dto/response/MemberResponse.java create mode 100644 week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/member/entity/Member.java create mode 100644 week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/member/repository/JpaMemberRepository.java create mode 100644 week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/member/service/MemberService.java create mode 100644 week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/post/controller/PostController.java create mode 100644 week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/post/controller/dto/request/CreatePostRequest.java create mode 100644 week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/post/controller/dto/request/UpdatePostRequest.java create mode 100644 week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/post/controller/dto/response/PostResponse.java create mode 100644 week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/post/entity/Post.java create mode 100644 week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/post/repository/PostRepository.java create mode 100644 week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/post/service/PostService.java diff --git "a/week8/[8\354\243\274\354\260\250]\354\240\225\354\234\244\354\243\274.md" "b/week8/[8\354\243\274\354\260\250]\354\240\225\354\234\244\354\243\274.md" new file mode 100644 index 0000000..6416457 --- /dev/null +++ "b/week8/[8\354\243\274\354\260\250]\354\240\225\354\234\244\354\243\274.md" @@ -0,0 +1,63 @@ +# 8주차 학습 내용 (CRUD API 연습) + +## 이론(공부해서 각 항목 아래에 작성해주세요!) +### REST API +REST(Representational State Transfer)는 웹의 자원을 HTTP를 통해 표현(Representation)하고, +이를 상태(State)를 전이(Transfer)하는 구조적 스타일. +>REST API는 REST 원칙을 따르는 API로, 자원(Resource), 메소드(HTTP Method), 표현(Representation) 세 요소로 구성됨 +> - 자원: URI로 표현 (예: /users, /posts/1) +> - 메소드: HTTP 메소드 사용 (GET, POST, PUT, DELETE 등) +> - 표현: JSON, XML 등으로 데이터 전달 + +### REST API 설계 주의 점(동사를 써도 되는 경우) +- 기본 원칙: URI에는 명사가 들어가야 하며, 동사는 사용하지 않는 것이 원칙 +- 예 + - /users (사용자 목록 조회) (o) + - /getUsers, /createUser (x) +- 다음과 같은 경우는 동사 사용 가능 + - 비표준 작업이 필요한 경우 (CRUD 외 작업) + - 예: /users/{id}/reset-password, /posts/{id}/like +### HTTP 특징 +- 비연결성(Connectionless): 요청 후 연결 종료 +- 무상태성(Stateless): 요청 간 상태 정보 저장 안 함 +- 클라이언트-서버 구조: 역할 분리 +- 캐시 가능: 응답을 캐싱하여 성능 향상 +- 계층화 구조: 중간 서버(프록시, 게이트웨이 등) 존재 가능 +### HTTP 메소드 8가지 +- GET: 리소스 조회(읽기 전용) +- POST: 리소스 생성 +- PUT: 리소스 전체 수정(대체) +- PATCH: 리소스 일부 수정 +- DELETE: 리소스 삭제 +- HEAD: GET과 동일하지만, 응답 본문 없음 (메타정보 확인용) +- OPTIONS: 서버가 지원하는 메소드 확인 +- TRACE: 요청-응답 루프를 테스트 +### DB +**정규화 3단계** +1. 1NF (제1 정규형) +- 중복 데이터 제거 +- 컬럼은 원자값(더 이상 나눌 수 없는 값)만 가짐 +2. 2NF (제2 정규형) +- 부분 종속 제거 +- 기본키의 일부분에만 종속된 속성 제거 +3. 3NF (제3 정규형) +- 이행적 종속 제거 +- 기본키가 아닌 속성에 종속된 속성 제거 +- +**관계** +- 1:1 (일대일) + - 한 테이블의 행이 다른 테이블의 하나의 행만 연결 +- 1:M (일대다) + - 하나의 행이 여러 행과 연결 가능 +- M:M (다대다) + - 두 테이블의 행이 서로 여러 개와 연결 가능 + - 별도의 조인 테이블로 분해해야 함 + +**PK, FK** +- PK + - 테이블의 고유값 식별자 + - 중복 x, NULL x +- FK + - 다른 테이블의 PK를 참조 + - 테이블 간 관계 형성 + - 삭제/수정 시 제약조건 설정 가능 (ON DELETE CASCADE 등) \ No newline at end of file diff --git a/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/comment/dto/CommentController.java b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/comment/dto/CommentController.java new file mode 100644 index 0000000..3bf11f1 --- /dev/null +++ b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/comment/dto/CommentController.java @@ -0,0 +1,41 @@ +package practice.dbswncrudapi.comment.dto; + +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import practice.dbswncrudapi.comment.dto.request.CommentRequest; +import practice.dbswncrudapi.comment.dto.response.CommentResponse; +import practice.dbswncrudapi.comment.service.CommentService; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/posts/{postId}/comments") +public class CommentController { + + private final CommentService commentService; + + @PostMapping + public ResponseEntity create(@PathVariable Long postId, @RequestBody CommentRequest dto) { + return ResponseEntity.ok(commentService.createComment(postId, dto)); + } + + @GetMapping + public ResponseEntity> getByPost(@PathVariable Long postId) { + return ResponseEntity.ok(commentService.getCommentsByPost(postId)); + } + + @PutMapping("/{commentId}") + public ResponseEntity update(@PathVariable Long commentId, @RequestBody CommentRequest dto) { + commentService.updateComment(commentId, dto.getContent()); + return ResponseEntity.ok().build(); + } + + @DeleteMapping("/{commentId}") + public ResponseEntity delete(@PathVariable Long commentId) { + commentService.deleteComment(commentId); + return ResponseEntity.noContent().build(); + } +} + diff --git a/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/comment/dto/request/CommentRequest.java b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/comment/dto/request/CommentRequest.java new file mode 100644 index 0000000..b3897f3 --- /dev/null +++ b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/comment/dto/request/CommentRequest.java @@ -0,0 +1,11 @@ +package practice.dbswncrudapi.comment.dto.request; + +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class CommentRequest { + private String content; + private Long userId; +} diff --git a/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/comment/dto/response/CommentResponse.java b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/comment/dto/response/CommentResponse.java new file mode 100644 index 0000000..cc8531e --- /dev/null +++ b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/comment/dto/response/CommentResponse.java @@ -0,0 +1,13 @@ +package practice.dbswncrudapi.comment.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class CommentResponse { + private Long id; + private String content; + private Long userId; + private Long postId; +} diff --git a/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/comment/entity/Comment.java b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/comment/entity/Comment.java new file mode 100644 index 0000000..2758e23 --- /dev/null +++ b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/comment/entity/Comment.java @@ -0,0 +1,40 @@ +package practice.dbswncrudapi.comment.entity; + +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import practice.dbswncrudapi.member.entity.Member; +import practice.dbswncrudapi.post.entity.Post; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Comment { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String content; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "post_id") + private Post post; + + @Builder + public Comment(String content, Member member, Post post) { + this.content = content; + this.member = member; + this.post = post; + } + + public void update(String content) { + this.content = content; + } +} diff --git a/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/comment/repository/CommentRepository.java b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/comment/repository/CommentRepository.java new file mode 100644 index 0000000..c04b7e9 --- /dev/null +++ b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/comment/repository/CommentRepository.java @@ -0,0 +1,12 @@ +package practice.dbswncrudapi.comment.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import practice.dbswncrudapi.comment.entity.Comment; + +import java.util.List; + +@Repository +public interface CommentRepository extends JpaRepository { + List findByPostId(Long postId); +} diff --git a/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/comment/service/CommentService.java b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/comment/service/CommentService.java new file mode 100644 index 0000000..ec6cc8c --- /dev/null +++ b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/comment/service/CommentService.java @@ -0,0 +1,56 @@ +package practice.dbswncrudapi.comment.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import practice.dbswncrudapi.comment.dto.request.CommentRequest; +import practice.dbswncrudapi.comment.dto.response.CommentResponse; +import practice.dbswncrudapi.comment.entity.Comment; +import practice.dbswncrudapi.comment.repository.CommentRepository; +import practice.dbswncrudapi.member.entity.Member; +import practice.dbswncrudapi.member.repository.JpaMemberRepository; +import practice.dbswncrudapi.post.entity.Post; +import practice.dbswncrudapi.post.repository.PostRepository; + +import java.util.List; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class CommentService { + private final CommentRepository commentRepository; + private final JpaMemberRepository jpaMemberRepository; + private final PostRepository postRepository; + + public CommentResponse createComment(Long postId, CommentRequest dto) { + Member member = jpaMemberRepository.findById(dto.getUserId()) + .orElseThrow(() -> new RuntimeException("사용자 없음")); + Post post = postRepository.findById(postId) + .orElseThrow(() -> new RuntimeException("게시글 없음")); + + Comment comment = Comment.builder() + .content(dto.getContent()) + .member(member) + .post(post) + .build(); + Comment saved = commentRepository.save(comment); + + return new CommentResponse(saved.getId(), saved.getContent(), member.getId(), post.getId()); + } + + public List getCommentsByPost(Long postId) { + return commentRepository.findByPostId(postId).stream() + .map(c -> new CommentResponse(c.getId(), c.getContent(), c.getMember().getId(), c.getPost().getId())) + .collect(Collectors.toList()); + } + + public void updateComment(Long commentId, String content) { + Comment comment = commentRepository.findById(commentId) + .orElseThrow(() -> new RuntimeException("댓글 없음")); + comment.update(content); + } + + public void deleteComment(Long commentId) { + commentRepository.deleteById(commentId); + } +} + diff --git a/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/member/controller/MemberController.java b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/member/controller/MemberController.java new file mode 100644 index 0000000..0958af2 --- /dev/null +++ b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/member/controller/MemberController.java @@ -0,0 +1,37 @@ +package practice.dbswncrudapi.member.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; +import practice.dbswncrudapi.member.controller.dto.request.MemberRequest; +import practice.dbswncrudapi.member.controller.dto.response.MemberResponse; +import practice.dbswncrudapi.member.service.MemberService; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/members") +public class MemberController { + + private final MemberService memberService; + + @PostMapping + public void signUp(@RequestBody MemberRequest memberRequest) { + memberService.signUp(memberRequest); + } + + @GetMapping("/{id}") + public MemberResponse getMember(@PathVariable Long id) { + return memberService.getMember(id); + } + + @PutMapping("/{id}") + public void updateMember(@PathVariable Long id, @RequestBody MemberRequest memberRequest) { + memberService.updateMember(id, memberRequest); + } + + @DeleteMapping("/{id}") + public void deleteMember(@PathVariable Long id) { + memberService.deleteMember(id); + } +} + + diff --git a/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/member/controller/dto/request/MemberRequest.java b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/member/controller/dto/request/MemberRequest.java new file mode 100644 index 0000000..a39da28 --- /dev/null +++ b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/member/controller/dto/request/MemberRequest.java @@ -0,0 +1,20 @@ +package practice.dbswncrudapi.member.controller.dto.request; + +import lombok.Getter; +import practice.dbswncrudapi.member.entity.Member; + +@Getter +public class MemberRequest { + private String username; + private String email; + private String password; + + public Member toEntity() { + return Member.builder() + .username(username) + .email(email) + .password(password) + .build(); + } +} + diff --git a/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/member/controller/dto/response/MemberResponse.java b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/member/controller/dto/response/MemberResponse.java new file mode 100644 index 0000000..9764ecc --- /dev/null +++ b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/member/controller/dto/response/MemberResponse.java @@ -0,0 +1,27 @@ +package practice.dbswncrudapi.member.controller.dto.response; + +import lombok.Builder; +import lombok.Getter; +import practice.dbswncrudapi.member.entity.Member; + +@Getter +public class MemberResponse { + private Long id; + private String username; + private String email; + + @Builder + public MemberResponse(Long id, String username, String email) { + this.id = id; + this.username = username; + this.email = email; + } + + public static MemberResponse from(Member member) { + return MemberResponse.builder() + .id(member.getId()) + .username(member.getUsername()) + .email(member.getEmail()) + .build(); + } +} diff --git a/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/member/entity/Member.java b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/member/entity/Member.java new file mode 100644 index 0000000..e47a125 --- /dev/null +++ b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/member/entity/Member.java @@ -0,0 +1,43 @@ +package practice.dbswncrudapi.member.entity; + +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import practice.dbswncrudapi.post.entity.Post; + +import java.util.ArrayList; +import java.util.List; + +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +public class Member { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "member_id") + private Long id; + + private String username; + private String email; + private String password; + + @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) + private List posts = new ArrayList<>(); + + @Builder + public Member(String username, String email, String password) { + this.username = username; + this.email = email; + this.password = password; + } + + public void update(String username, String email, String password) { + if (username != null) this.username = username; + if (email != null) this.email = email; + if (password != null) this.password = password; + } +} + diff --git a/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/member/repository/JpaMemberRepository.java b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/member/repository/JpaMemberRepository.java new file mode 100644 index 0000000..1fe3d88 --- /dev/null +++ b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/member/repository/JpaMemberRepository.java @@ -0,0 +1,11 @@ +package practice.dbswncrudapi.member.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import practice.dbswncrudapi.member.entity.Member; + +import java.util.Optional; + +public interface JpaMemberRepository extends JpaRepository { + + Optional findById(Long id); +} \ No newline at end of file diff --git a/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/member/service/MemberService.java b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/member/service/MemberService.java new file mode 100644 index 0000000..13c4ce6 --- /dev/null +++ b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/member/service/MemberService.java @@ -0,0 +1,41 @@ +package practice.dbswncrudapi.member.service; + +import org.springframework.transaction.annotation.Transactional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import practice.dbswncrudapi.member.controller.dto.request.MemberRequest; +import practice.dbswncrudapi.member.controller.dto.response.MemberResponse; +import practice.dbswncrudapi.member.entity.Member; +import practice.dbswncrudapi.member.repository.JpaMemberRepository; + +@Service +@RequiredArgsConstructor +@Transactional +public class MemberService { + + private final JpaMemberRepository jpaMemberRepository; + + public void signUp(MemberRequest memberRequest) { + Member member = memberRequest.toEntity(); + jpaMemberRepository.save(member); + } + + @Transactional(readOnly = true) + public MemberResponse getMember(Long id) { + Member member = jpaMemberRepository.findById(id) + .orElseThrow(() -> new IllegalArgumentException("해당 사용자가 존재하지 않습니다.")); + return MemberResponse.from(member); + } + + public void updateMember(Long id, MemberRequest request) { + Member member = jpaMemberRepository.findById(id) + .orElseThrow(() -> new IllegalArgumentException("해당 사용자가 존재하지 않습니다.")); + member.update(request.getUsername(), request.getEmail(), request.getPassword()); + } + + public void deleteMember(Long id) { + Member member = jpaMemberRepository.findById(id) + .orElseThrow(() -> new IllegalArgumentException("해당 사용자가 존재하지 않습니다.")); + jpaMemberRepository.delete(member); + } +} diff --git a/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/post/controller/PostController.java b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/post/controller/PostController.java new file mode 100644 index 0000000..ca53294 --- /dev/null +++ b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/post/controller/PostController.java @@ -0,0 +1,48 @@ +package practice.dbswncrudapi.post.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; +import practice.dbswncrudapi.post.controller.dto.request.CreatePostRequest; +import practice.dbswncrudapi.post.controller.dto.request.UpdatePostRequest; +import practice.dbswncrudapi.post.controller.dto.response.PostResponse; +import practice.dbswncrudapi.post.service.PostService; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/posts") +public class PostController { + + private final PostService postService; + + @PostMapping + public void createPost(@RequestBody CreatePostRequest createPostRequest) { + postService.createPost(createPostRequest); + } + + @GetMapping + public List getAllPosts() { + return postService.getAllPosts(); + } + + @GetMapping("/{id}") + public PostResponse getPost(@PathVariable Long id) { + return postService.getPost(id); + } + + @GetMapping("/user/{userId}") + public List getPostsByUser(@PathVariable Long userId) { + return postService.getPostsByUserId(userId); + } + + @PutMapping("/{id}") + public void updatePost(@PathVariable Long id, @RequestBody UpdatePostRequest updatePostRequest) { + postService.updatePost(id, updatePostRequest); + } + + @DeleteMapping("/{id}") + public void deletePost(@PathVariable Long id) { + postService.deletePost(id); + } +} diff --git a/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/post/controller/dto/request/CreatePostRequest.java b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/post/controller/dto/request/CreatePostRequest.java new file mode 100644 index 0000000..559a3da --- /dev/null +++ b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/post/controller/dto/request/CreatePostRequest.java @@ -0,0 +1,20 @@ +package practice.dbswncrudapi.post.controller.dto.request; + +import lombok.Getter; +import practice.dbswncrudapi.member.entity.Member; +import practice.dbswncrudapi.post.entity.Post; + +@Getter +public class CreatePostRequest { + private Long userId; + private String title; + private String content; + + public Post toEntity(Member member) { + return Post.builder() + .title(title) + .content(content) + .member(member) + .build(); + } +} \ No newline at end of file diff --git a/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/post/controller/dto/request/UpdatePostRequest.java b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/post/controller/dto/request/UpdatePostRequest.java new file mode 100644 index 0000000..7e72412 --- /dev/null +++ b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/post/controller/dto/request/UpdatePostRequest.java @@ -0,0 +1,9 @@ +package practice.dbswncrudapi.post.controller.dto.request; + +import lombok.Getter; + +@Getter +public class UpdatePostRequest { + private String title; + private String content; +} \ No newline at end of file diff --git a/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/post/controller/dto/response/PostResponse.java b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/post/controller/dto/response/PostResponse.java new file mode 100644 index 0000000..8bb82a8 --- /dev/null +++ b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/post/controller/dto/response/PostResponse.java @@ -0,0 +1,30 @@ +package practice.dbswncrudapi.post.controller.dto.response; + +import lombok.Builder; +import lombok.Getter; +import practice.dbswncrudapi.post.entity.Post; + +@Getter +public class PostResponse { + private Long id; + private String title; + private String content; + private Long userId; + + @Builder + public PostResponse(Long id, String title, String content, Long userId) { + this.id = id; + this.title = title; + this.content = content; + this.userId = userId; + } + + public static PostResponse from(Post post) { + return PostResponse.builder() + .id(post.getId()) + .title(post.getTitle()) + .content(post.getContent()) + .userId(post.getMember().getId()) + .build(); + } +} \ No newline at end of file diff --git a/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/post/entity/Post.java b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/post/entity/Post.java new file mode 100644 index 0000000..c6cad78 --- /dev/null +++ b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/post/entity/Post.java @@ -0,0 +1,38 @@ +package practice.dbswncrudapi.post.entity; + +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import practice.dbswncrudapi.member.entity.Member; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Post { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "post_id") + private Long id; + + private String title; + private String content; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + @Builder + public Post(String title, String content, Member member) { + this.title = title; + this.content = content; + this.member = member; + } + + public void update(String title, String content) { + if (title != null) this.title = title; + if (content != null) this.content = content; + } +} diff --git a/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/post/repository/PostRepository.java b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/post/repository/PostRepository.java new file mode 100644 index 0000000..4789031 --- /dev/null +++ b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/post/repository/PostRepository.java @@ -0,0 +1,14 @@ +package practice.dbswncrudapi.post.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import practice.dbswncrudapi.post.entity.Post; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface PostRepository extends JpaRepository { + @Override + Optional findById(Long id); +} diff --git a/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/post/service/PostService.java b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/post/service/PostService.java new file mode 100644 index 0000000..2429848 --- /dev/null +++ b/week8/dbswn-crud-api/src/main/java/practice/dbswncrudapi/post/service/PostService.java @@ -0,0 +1,66 @@ +package practice.dbswncrudapi.post.service; + +import org.springframework.transaction.annotation.Transactional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import practice.dbswncrudapi.member.entity.Member; +import practice.dbswncrudapi.member.repository.JpaMemberRepository; +import practice.dbswncrudapi.post.controller.dto.request.CreatePostRequest; +import practice.dbswncrudapi.post.controller.dto.request.UpdatePostRequest; +import practice.dbswncrudapi.post.controller.dto.response.PostResponse; +import practice.dbswncrudapi.post.entity.Post; +import practice.dbswncrudapi.post.repository.PostRepository; + +import java.util.List; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +@Transactional +public class PostService { + + private final JpaMemberRepository jpaMemberRepository; + private final PostRepository postRepository; + + public void createPost(CreatePostRequest createPostRequest) { + Member member = jpaMemberRepository.findById(createPostRequest.getUserId()) + .orElseThrow(() -> new IllegalArgumentException("해당 사용자가 존재하지 않습니다.")); + Post post = createPostRequest.toEntity(member); + postRepository.save(post); + } + + @Transactional(readOnly = true) + public List getAllPosts() { + return postRepository.findAll().stream() + .map(PostResponse::from) + .collect(Collectors.toList()); + } + + @Transactional(readOnly = true) + public PostResponse getPost(Long id) { + Post post = postRepository.findById(id) + .orElseThrow(() -> new IllegalArgumentException("게시글이 존재하지 않습니다.")); + return PostResponse.from(post); + } + + @Transactional(readOnly = true) + public List getPostsByUserId(Long userId) { + Member member = jpaMemberRepository.findById(userId) + .orElseThrow(() -> new IllegalArgumentException("사용자가 존재하지 않습니다.")); + return member.getPosts().stream() + .map(PostResponse::from) + .collect(Collectors.toList()); + } + + public void updatePost(Long id, UpdatePostRequest updatePostRequest) { + Post post = postRepository.findById(id) + .orElseThrow(() -> new IllegalArgumentException("게시글이 존재하지 않습니다.")); + post.update(updatePostRequest.getTitle(), updatePostRequest.getContent()); + } + + public void deletePost(Long id) { + Post post = postRepository.findById(id) + .orElseThrow(() -> new IllegalArgumentException("게시글이 존재하지 않습니다.")); + postRepository.delete(post); + } +}