From 66ad03ee46d6386f9e5303d46e2631024e7b0f6f Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Wed, 30 Apr 2025 20:35:59 +0900 Subject: [PATCH 01/33] =?UTF-8?q?docs:=203=EC=A3=BC=EC=B0=A8=20=EB=A6=AC?= =?UTF-8?q?=EB=93=9C=EB=AF=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0]\354\213\240\355\230\204\354\247\204.md" | 2 +- ...0]\354\213\240\355\230\204\354\247\204.md" | 68 +++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 "week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" diff --git "a/week3/[3\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" "b/week3/[3\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" index 88b8298..311fa77 100644 --- "a/week3/[3\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" +++ "b/week3/[3\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" @@ -17,7 +17,7 @@ -> 스프링이 알아서 객체를 만들어서 넣어줌 -- @Component: 해당 어노테이션을 붙인 클래스 -> 이 클래스를 스프링이 자동으로 객체로 만들어 관리해줘 +- @Component: 해당 어노테이션을 붙인 클래스 -> 이 클래스를 스프링이 자동으로 객체로 만들어 관리해줌 - Bean: 스프링이 관리하는 객체들 - @Autowired: 등록된 Bean을 주입해주라는 어노테이션 -> 야 스프링 여기에 적절한 Bean을 넣어라 diff --git "a/week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" "b/week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" new file mode 100644 index 0000000..46f7f12 --- /dev/null +++ "b/week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" @@ -0,0 +1,68 @@ +### Spring MVC Lifecycle +(Request) => Filter -> DispatcherServlet -> HandlerMapping -> HandlerInterceptor -> Controller -> Service -> Repository -> ViewResolver + + +- **Filter** + - 웹 요청이 들어올 때 맨 처음 가로채서, 필요한 검사나 설정을 하고 난 후 다음 단계로 넘기는 역할 + - 인증/인가: 로그인 했는지 확인 후, 안했으면 리다이렉트 + - 로깅: 요청 URL, 요청자 IP 등을 로그로 남김 + - 인코딩 설정 + - 보안 검사: XSS나 SQL Injection에 대한 간단 필터링 + - SQL Injection: 사용자가 쿼리문 안에 악의적인 코드를 주입해 DB를 속이는 공격 + - CORS 설정 + + +- **DispatcherServlet** + - HandlerMapping에게 요청을 어떤 Controller가 처리해야 하는지를 물어봄 + - HandlerMapping이 알려준 Controller를 호출함 + +- **HandlerMapping** + - DispatcherServlet이 받은 요청에 대해 어떤 Controller가 처리할지를 결정함 + +- **HandlerInterceptor** + - Controller 앞에 있는 문지기 역할 + - 다음 타이밍에 개입 가능 + - preHandle(): 컨트롤러가 실행되기 직전 + - posthandle(): 컨트롤러 실행 직후, 뷰 렌더링 전 + - afterCompletion(): 뷰 렌더링까지 완료된 후 + + - 흐름 순서 + - 요청 -> Filter -> DispatcherServlet -> HandlerInterceptor (preHandle) -> Controller 실행 -> HandlerInterceptor (postHandle) -> View 렌더링 -> HandlerInterceptor (afterCompletion) -> 응답 반환 + +- **Controller** + - 요청과 매핑되는 곳 + - 어떤 로직으로 처리할 것인지 결정하고 그에 맞는 Service를 호출함 + +- **Service** + - 데이터 처리 및 가공을 위한 비즈니스 로직을 수행 + - 요청에 대한 실질적인 로직을 수행함 + - Repository를 통해 DB에 접근해 데이터의 CRUD를 처리함 + +- **Repository** + - DB에 접근하는 객체 + - DAO(Data Access Ovject)의 발전된 개념 + - DB 접근 뿐 아니라 도메인 중심 아키텍처에 어울리게 설계된 Spring 스타일의 DAO + - Service에서 DB에 접근할 수 있게 해 데이터의 CRUD를 도와줌 + +- **ViewResolver** + - Controller에서 준 뷰의 이름을 DispatcherServlet으로부터 넘겨받음 + - 해당 뷰를 렌더링하고 DispatcherServlet으로 리턴 + - DispatcherServlet에서는 해당 뷰 화면을 응답 + - View 없이 데이터만 전달하는 경우 ViewResolver는 불필요함 + + +### Dispatcher servlet의 역할 +1. 요청 수신 + - 클라이언트의 HTTP 요청을 Filter 다음으로 받음 +2. 요청 URL에 따라 HandlerMapping에게 어떤 Controller가 해당 요청을 처리할지 물어봄 +3. HandlerAdapter 호출 + - 해당 Controller를 실제로 실행할 수 있는 방법을 HandlerAdapter에게 위임해 호출함 +4. Controller 실행 +5. ViewResolver로 어떤 화면을 보여줄지 결정 +6. 클라이언트에게 최종 응답 + +### Bean이란? +### Bean Lifecycle +### Spring 어노테이션 10가지와 그에 대한 설명 +### Spring 의존성 주입 방식 +### 생성자 주입 방식(중요) From 810c0f289e65c39a1a48b2ca078ad9d28fcbf407 Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Wed, 7 May 2025 16:39:02 +0900 Subject: [PATCH 02/33] =?UTF-8?q?docs:=203=EC=A3=BC=EC=B0=A8=20=EB=A6=AC?= =?UTF-8?q?=EB=93=9C=EB=AF=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0]\354\213\240\355\230\204\354\247\204.md" | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git "a/week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" "b/week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" index 46f7f12..62e9683 100644 --- "a/week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" +++ "b/week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" @@ -62,6 +62,32 @@ 6. 클라이언트에게 최종 응답 ### Bean이란? +스프링이 관리하는 객체(인스턴스) +- 개발자가 new 키워드로 직접 만드는 객체가 아니라 스프링이 자동으로 생성하고 관리하는 객체 + + +- 특징 + - 객체의 생명주기를 스프링이 책임짐 + - 필요할 때 자동 주입 가능 + - 전역적으로 공유되어 사용됨 + +``` +@Component // Bean으로 등록됨 +public class UserService { + ... +} + +``` + +``` +@Configuration +public class AppConfig { + @Bean + public UserService userService() { + return new UserService(); + } +} +``` ### Bean Lifecycle ### Spring 어노테이션 10가지와 그에 대한 설명 ### Spring 의존성 주입 방식 From c0039d723c664f5624693e7758638e3fe6197b05 Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Wed, 7 May 2025 16:41:31 +0900 Subject: [PATCH 03/33] =?UTF-8?q?docs:=20Bean=20=EA=B0=9C=EB=85=90=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" | 1 + 1 file changed, 1 insertion(+) diff --git "a/week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" "b/week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" index 62e9683..28343c8 100644 --- "a/week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" +++ "b/week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" @@ -88,6 +88,7 @@ public class AppConfig { } } ``` + ### Bean Lifecycle ### Spring 어노테이션 10가지와 그에 대한 설명 ### Spring 의존성 주입 방식 From a09332e913ebc52fa9aa528ca8297127d269062f Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Wed, 7 May 2025 16:45:06 +0900 Subject: [PATCH 04/33] =?UTF-8?q?docs:=20Bean=20Lifecycle=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...60\250]\354\213\240\355\230\204\354\247\204.md" | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git "a/week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" "b/week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" index 28343c8..44f8725 100644 --- "a/week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" +++ "b/week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" @@ -90,6 +90,20 @@ public class AppConfig { ``` ### Bean Lifecycle +: 스프링 컨테이너가 Bean을 생성/초기화/사용/소멸시키기까지의 전 과정 + +1. 객체 생성 + - 스프링이 객체 인스턴스를 만듦 +2. 의존성 주입 + - @Autowired, 생성자 주입 등을 통해 필요한 의존 객체를 주입 +3. 초기화 + - 초기 설정 작업 진행 +4. 사용 + - 실제로 해당 Bean을 다른 컴포넌트에서 사용 +5. 소멸 + - @PreDstrosy 등이 호출되어 정리 작업이 수행됨 + + ### Spring 어노테이션 10가지와 그에 대한 설명 ### Spring 의존성 주입 방식 ### 생성자 주입 방식(중요) From fada9b8e609915ff6d5ce5e257939274763f7fee Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Wed, 7 May 2025 16:55:45 +0900 Subject: [PATCH 05/33] =?UTF-8?q?docs:=20=EC=8A=A4=ED=94=84=EB=A7=81=20?= =?UTF-8?q?=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0]\354\213\240\355\230\204\354\247\204.md" | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git "a/week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" "b/week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" index 44f8725..97847ef 100644 --- "a/week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" +++ "b/week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" @@ -89,8 +89,9 @@ public class AppConfig { } ``` + ### Bean Lifecycle -: 스프링 컨테이너가 Bean을 생성/초기화/사용/소멸시키기까지의 전 과정 +스프링 컨테이너가 Bean을 생성/초기화/사용/소멸시키기까지의 전 과정 1. 객체 생성 - 스프링이 객체 인스턴스를 만듦 @@ -105,5 +106,44 @@ public class AppConfig { ### Spring 어노테이션 10가지와 그에 대한 설명 +**@Component** +- 스프링이 Bean으로 등록할 수 있게 해주는 클래스 표시용 어노테이션 + +**@Service** +- 비즈니스 로직을 담는 서비스 계층 클래스에 붙임 + - 내부적으로는 @Component와 같지만 의미적으로 구분함 + +**@Repository** +- DAO 역할을 하는 클래스에 붙임 + - @Component와 같지만, 데이터 접근 예외를 스프링 예외로 변환해줌 + +**@Controller** +- 웹 요청을 처리하는 클래스에 붙임 +- 스프링 MVC에서 클라이언트 요청을 받는 진입점 + +**@RestController** +- @Controller + @ResponseBody +- JSON 형태로 데이터를 반환하는 API 작성 시 사용 + +**@Autowired** +- 필요한 Bean을 자동 주입 +- 생성자, 필드, 메서드에 붙일 수 있음 + +**@Qualifier** +- @Autowired와 함께 사용할 때, 여러 Bean 중 어떤 Bean을 주입할 지 이름으로 지정함 + +**@Value** +- 환경 변수에서 값을 읽어 주입할 때 사용 + +**@Configuration** +- 설정 클래스를 나타냄 +- 내부에서 @Bean을 사용해 수동으로 Bean을 등록하는 것이 가능 + +**@Bean** +- 메서드에 붙임 + - 직접 생성한 객체를 Bean으로 등록할 때 사용 +- @Configuration 클래스 내부에서 사용함 + + ### Spring 의존성 주입 방식 ### 생성자 주입 방식(중요) From c56174498c35e3613ee94204ee2e995b1ff6197c Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Wed, 7 May 2025 17:00:28 +0900 Subject: [PATCH 06/33] =?UTF-8?q?docs:=20=EC=8A=A4=ED=94=84=EB=A7=81=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=A3=BC=EC=9E=85=20=EB=B0=A9?= =?UTF-8?q?=EC=8B=9D=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0]\354\213\240\355\230\204\354\247\204.md" | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git "a/week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" "b/week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" index 97847ef..67a1f1b 100644 --- "a/week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" +++ "b/week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" @@ -146,4 +146,87 @@ public class AppConfig { ### Spring 의존성 주입 방식 +- **생성자 주입(Constructor Injection)**: 생성자를 통해 의존성을 주입 (생성자에 @Autowired) + + ``` + @Component + public class OrderService { + + private final PaymentService paymentService; + + @Autowired // 생략 가능 (스프링 4.3 이상이면 1개 생성자는 자동으로 주입됨) + public OrderService(PaymentService paymentService) { + this.paymentService = paymentService; + } + } + ``` + - 장점 + - final로 불변성을 보장 + - mock 객체를 주입하는 것이 가능해져 테스트 용이 + - 컴파일 시간에 오류가 잡힘 + - 단점 + - 의존성이 많을 때 생성자의 매개변수가 길어짐 + + +- **필드 주입(Field Injection)**: @Autowired로 필드에 직접 주입 + + ``` + @Component + public class OrderService { + + @Autowired + private PaymentService paymentService; + } + ``` + - 장점 + - 코드가 짧아서 간단한 구조를 가짐 + - 러닝커브가 작음 + - 단점 + - private이기 때문에 mock으로 주입이 어려워 테스트에 어려움 + - final을 사용하지 못하기 때문에 불변성이 보장 X + - 스프링 컨테이너가 없이는 사용이 불가하기 때문에 다른 환경에서의 사용이 어려워짐 + + +- **세터 주입(Setter Injection)**: @Autowired가 붙은 setter 메서드로 주입 + + ``` + @Component + public class OrderService { + + private PaymentService paymentService; + + @Autowired + public void setPaymentService(PaymentService paymentService) { + this.paymentService = paymentService; + } + } + ``` + - 장점 + - 선택적으로 의존성을 주입하는 것이 가능 + - setter를 통해 Mock을 주입하는 것이 가능하기 때문에 테스트가 가능 + - 단점 + - 의존성이 필수인지 선택인지 명확하지 않음 + - 객체가 완전히 세팅되기 전까지는 불안정한 상태임 + + +- **일반 메서드 주입(Method Injection)**: 직접 만든 메서드에 의존성 주입 + + ``` + @Component + public class OrderService { + + private PaymentService paymentService; + + @Autowired + public void init(PaymentService paymentService) { + this.paymentService = paymentService; + } + } + ``` + - 장점 + - 설정 처리나 초기화 로직 등과 함께 의존성을 주입할 때 유용 + - 단점 + - 코드 가독성이 떨어짐 + + ### 생성자 주입 방식(중요) From f4c84eac24b8c79e012cf4134f5a504dd437a2ec Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Wed, 7 May 2025 17:03:36 +0900 Subject: [PATCH 07/33] =?UTF-8?q?docs:=20=EC=83=9D=EC=84=B1=EC=9E=90=20?= =?UTF-8?q?=EC=A3=BC=EC=9E=85=20=EB=B0=A9=EC=8B=9D=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...50]\354\213\240\355\230\204\354\247\204.md" | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git "a/week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" "b/week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" index 67a1f1b..8f77a57 100644 --- "a/week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" +++ "b/week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" @@ -230,3 +230,21 @@ public class AppConfig { ### 생성자 주입 방식(중요) +필요한 의존 객체를 생성자를 통해 주입받는 방식 => 객체가 생성될 때 부터 의존성을 강제하고, 안정적으로 주입받는 것이 가능 + +- Lombok 라이브러리 활용 시 + +``` + @RequiredArgsConstructor + @Service + public class OrderService { + + private final UserRepository userRepository; + private final OrderRepository orderRepository; + + // 생성자가 자동 생성되고, 스프링이 알아서 주입해줌 + } +``` +-> final 필드만 생성자 파라미터로 포함됨 => 불변성이 보장됨 + +- 스프링이 권장하는 가장 안전하고 테스트하기 쉬운 의존성 주입 방식 From 82c24652c45246a8a33883227b4c18916a296fb5 Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Wed, 7 May 2025 17:24:08 +0900 Subject: [PATCH 08/33] =?UTF-8?q?docs:=206=EC=A3=BC=EC=B0=A8=20=EB=A6=AC?= =?UTF-8?q?=EB=93=9C=EB=AF=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git "a/week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" "b/week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" index 8f77a57..ad9aa78 100644 --- "a/week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" +++ "b/week6/[6\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" @@ -247,4 +247,4 @@ public class AppConfig { ``` -> final 필드만 생성자 파라미터로 포함됨 => 불변성이 보장됨 -- 스프링이 권장하는 가장 안전하고 테스트하기 쉬운 의존성 주입 방식 +=> 스프링이 권장하는 가장 안전하고 테스트하기 쉬운 의존성 주입 방식 From ab721843ac2df7e3c527331b0d8f0c6a2a2b1791 Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Sun, 11 May 2025 17:41:53 +0900 Subject: [PATCH 09/33] =?UTF-8?q?docs:=207=EC=A3=BC=EC=B0=A8=20=EA=B3=BC?= =?UTF-8?q?=EC=A0=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0]\354\213\240\355\230\204\354\247\204.md" | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 "week7/[7\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" diff --git "a/week7/[7\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" "b/week7/[7\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" new file mode 100644 index 0000000..ff7b7b1 --- /dev/null +++ "b/week7/[7\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" @@ -0,0 +1,112 @@ +## JPA 개념 + +### JPA와 JDBC의 차이란? + +- JDBC(Java DB Connectivity): 자바에서 DB와 통신하기 위한 가장 기본적 api + - 역할: 자바 코드로 SQL을 작성하고 직접 실행하며 결과를 받아옴 + - 사용법 -> 직접 SQL문을 작성하고 Connection 등을 수동으로 개발자가 관리해야 함 + + +- JPA(Java Persistence API): 자바 객체를 DB에 저장하고 관리하게 해주는 추상화된 ORM 표준 api + - 역할: SQL을 직접 작성하지 않고, entity 클래스를 통해 데이터를 관리함 + - 사용법 -> @Entity, @Id, @column과 같은 어노테이션으로 DB 테이블과 객체를 연결함 + + +- 차이점 + - JDBC는 SQL 중심, JPA는 객체(entity) 중심 + - JDBC는 트랜잭션을 수동으로 직접 처리, JPA는 JPA가 트랜잭션을 자동으로 관리해줌 + - 코드 복잡도: JDBC -> 높음 / JPA -> 낮음 + - 유지보수: JDBC가 JPA보다 불편함 + + +### ORM이란 무엇이고 JPA가 ORM 프레임워크에서 어떤 역할을 하는지 + +- ORM(Object-Relational Mapping): 자바 객체(클래스)와 데이터베이스의 테이블을 자동으로 매핑해주는 기술 + - 직접 SQL을 쓰는 대신 자바 객체만 다루면 내부에서 알아서 SQL로 변환해 DB랑 통신해줌 + + +- JPA가 ORM에서 하는 역할 + - entity 매핑 (자바 클래스와 DB 테이블을 연결) + - CRUD 자동화 (SQL 없이 수행) + - 트랜잭션을 더 쉽게 처리 + + +- Hibernate: JPA를 구현한 실제 라이브러리 + +## 엔티티 +JPA에서 DB table에 매핑되는 자바 클래스 -> 하나의 entity는 DB의 한 테이블과 1:1로 매칭됨 + +- DB의 테이블 구조를 자바 객체로 표현해줌 +- DB row를 자바 인스턴스로 다룸 +- JPA가 entity를 통해 SQL을 자동으로 생성해 DB와 통신함 + +### 엔티티 필수 어노테이션의 종류는? + +- @Entity: 이 클래스가 JPA가 관리할 엔티티임을 선언해줌 +- @Id: 엔티티의 pk 지정 +- @GeneratedValue: pk의 자동 생성 전략 설정 +- @Column: 필드와 테이블의 컬럼을 매핑 + +### JPA에서 엔티티의 필수 조건은? + +1. @Entity 어노테이션 필수 +2. 기본 생성자(public / protected)가 있어야 함 +3. final 클래스/메서드 사용 금지 +4. 필드 또는 프로퍼티의 접근 방식을 일관되게 작성해야 함 +5. 식별자 == @Id가 반드시 있어야 함 + +### 엔티티의 생명주기란? + +- 비영속: 엔티티 객체는 생성되었지만 아직 영속성 컨텍스트에 저장되지 않은 상태 +- 영속: 영속성 컨텍스트에 저장되어 JPA가 관리하는 상태 +- 준영속: 영속성 컨텍스트에서 지운 상태 +- 삭제: 실제 DB 삭제를 요청한 상태 + +## 영속성 컨텍스트 + +- entity 객체들을 저장하고 관리하는 일종의 메모리 공간 +- JPA를 사용해 entity를 DB에 저장하기 전에 항상 영속성 컨텍스트에 먼저 저장해야 함 + - 영속성 컨텍스트에 entity를 저장한다고 해서 DB에 저장되는 것은 아님 + +- 필요성 + - 같은 트랜잭션 내에서 객체와 DB를 1:1로 연결해 중복 쿼리를 방지함 + - 변경 사항을 추적해 자동으로 DB에 반영할 수 있도록 도와줌 + +### 영속성 컨텍스트 상태의 종류는? + +- 트랜잭션 스코프: 트랜잭션 단위로 영속성 컨텍스트가 생성되고 종료됨 +- 확장 스코프: 트랜잭션을 넘어 지속적으로 유지됨 + +## 연간관계 +두 entity 간의 참조 관계 (RDB에서 테이블 간의 fk 관계) + +### 연간관계의 종류와 JPA에서의 표현법은? +1. 방향에 따른 분류 + - 단방향 연관관계 (a->b, b는 a를 알지 못함) + - 양방향 연관관계 (a와 b가 서로 참조가 가능함) + - 반드시 한 쪽을 연관관계의 주인으로 설정해야 함. (fk를 설정하는 쪽이 한 군데여야하기 때문에) + +2. 다중성에 따른 분류 + - 1:1 -> 한 개당 한 개 + ``` + @OneToOne + @JoinColumn(name="id") + ``` + + - 1:n -> 한 개당 여러 개 + ``` + @OneToMany (mappedBy="member") + ``` + + - n:1 -> 여러 개가 한 개를 참조 (가장 많이 쓰임) + ``` + @ManyToOne + @JoinColumn(name="id") + ``` + + - n:m -> 여러 개가 여러 개와 연결 (잘 안쓰임) + ``` + @ManyToMany + @JoinTable(name="student_sub", joinCOlumns=@JoinColumn(name="student_id"), inverseJoinColumns = @JoinColumn(name="sub_id")) + ``` + From 6c54c241b265eb6036d4f116807cd0c924e06c3a Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Fri, 16 May 2025 19:21:45 +0900 Subject: [PATCH 10/33] =?UTF-8?q?docs:=208=EC=A3=BC=EC=B0=A8=20=EA=B3=BC?= =?UTF-8?q?=EC=A0=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0]\354\213\240\355\230\204\354\247\204.md" | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 "week8/[8\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" diff --git "a/week8/[8\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" "b/week8/[8\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" new file mode 100644 index 0000000..5db5c64 --- /dev/null +++ "b/week8/[8\354\243\274\354\260\250]\354\213\240\355\230\204\354\247\204.md" @@ -0,0 +1,131 @@ +### REST API +REST: REpresentational State Transfer + +- REST 구성 + - 자원: URI + - 행위: HTTP Method(GET, POST, PATCH, DELETE) + - 표현: Representation + - 어떤 자원의 특정 시점의 상태를 반영하고 있는 정보 + +-> 자원을 이름(=자원의 표현)으로 구분해 해당 자원의 상태를 주고 받는 모든 것 +=> 자원의 표현에 의한 상태 전달 + +- REST API: REST 규칙을 지킨 API + +### REST API 설계 주의점 (동사를 써도 되는 경우) +- URI는 정보의 자원을 표현해야 한다. + - `/createUser` (X) `/users` (O) +- 자원은 복수형을 사용한다. +- 계층적으로 구조표현을 한다. + - `/users/123/orders/456` +- 필터링, 페이지네이션 등은 쿼리 파라미터를 통한다. +- 에러처리에 명확한 HTTP 상태코드를 사용한다. +- 자원에 대한 행위는 HTTP Method로 표현한다. + - 행위는 URI에 포함하지 않는다. + + +- **동사를 써도 되는 경우** + - 특수한 행위나 행동 자체가 목적일 때 + - POST `/orders/456/cancel` + +### HTTP 특징 +- 클라이언트 서버 구조 + - 클라이언트는 서버에 Request를 보내고, Response를 대기한다. + - 서버가 Request에 대한 결과를 만들어 Response한다. + + -> 양쪽이 독립적으로 진행할 수 있다. + + +- stateless(무상태): 서버가 클라이언트의 상태를 보존하지 않음 + - 고객: 이 토마토 얼마에요? + - 점원: 100만원이에요 + - 고객: 토마토 2개 구매할게요 + - 점원: 토마토 2개는 200만원이에요 + + -> 요청할 때 필요한 데이터를 모두 포함해서 서버에게 요청하게 됨 + + - 특징 + - 갑자기 클라이언트의 요청이 증가해도 서버를 많이 투입할 수 있음 + - 응답 서버를 쉽게 바꿀 수 있음 + - 한계 + - 모든 것을 stateless로 설계할 수 있는 경우도 있고 없는 경우도 있음 + - stateless: 서비스 소개 화면 + - stateful: 로그인 + - 데이터를 너무 많이 보냄 + + +- Connectionless(비연결성) + - 자원에 대한 요청을 주고 받을 때만 연결을 유지해 최소한으로 사용하고, 클라이언트와 요청-응답 과정 후 연결을 끊음 + + -> 서버 자원을 효율적으로 사용할 수 있음 + + - 한계 + - TCP/IP 연결을 새로 맺어야 하기 때문에 3way handshake 시간이 추가됨 + -> HTTP 지속 연결(Persistent Connections)로 문제를 해결함 + + +- 단순하고 확장이 가능함 + +### HTTP 메소드 8가지 +- GET: 자원 조회 + - 서버 상태를 바꾸지 않음 + - 멱등성 가짐 (여러 번 호출해도 결과가 같음) +- POST: 주로 등록에 사용함 + - 비멱등성 가짐 (같은 요청을 여러 번 시도하면 자원의 중복 생성이 가능) +- PUT: 자원을 대체하고, 해당하는 자원이 없다면 새로 생성 +- PATCH: 자원을 부분적으로 변경함 +- DELETE: 자원 삭제 +- HEAD: GET 요청과 동일하지만 body를 제외한 헤더 정보만 응답해줌 +- OPTIONS: 요청 가능한 HTTP 메서드 목록을 조회 + - 서버가 허용하는 동작을 확인하기 위한 용도로 사용 +- TRACE: 클라이언트 -> 서버로 가는 요청을 그대로 반사해 확인 + - 경로를 추적해 네트워크 문제를 확인하는데, 실제 사용은 거의 없음 + +### DB + +#### 정규화 3단계 +1. 1NF(Normal Form) + - 각 컬럼은 더 이상 쪼갤 수 없는 원자값만 가져야 함 -> 하나의 셀에는 하나의 값만 존재해야 함 +2. 2NF + - 1NF를 만족하면서 기본 키의 부분 집합에 종속된 속성을 제거 + - ex) 과목명은 과목 코드에만 종속되어있고 학번과는 무관한데 하나의 테이블에 학번,과목코드,과목명이 함께 있다면 + (PK = 학번, 과목 코드) + -> 학번-과목 코드 테이블/과목코드-과목명 테이블로 분리 + +3. 3NF + - 2NF를 만족하면서 기본 키가 아닌 컬럼에 의해 결정되는 다른 컬럼을 제거 + - ex) 부서명은 부서 ID에 의해 결정되는데 한 테이블에 사원 ID, 부서 ID, 부서명이 있다면 + (PK = 사원 ID) + -> 사원 ID-부서 ID 테이블/부서 ID-부서명 테이블로 분리 + + +#### 1:1 +- A 테이블의 하나의 레코드는 B 테이블의 하나의 레코드와 정확히 연결됨 + + ex) 한 사람은 하나의 주민등록증만 가짐 + +#### 1:M +- A 테이블의 하나의 레코드는 B 테이블의 여러 레코드와 연결될 수 있음 + + ex) 한 고객이 여러 개의 주문을 할 수 있음 + +#### M:N +- A와 B 테이블 각각의 레코드가 서로 여러 개와 연결될 수 있음 + + ex) 학생은 여러 강의를 들을 수 있고, 한 강의에 여러 학생이 참여할 수 있음 + +#### PK, FK +- PK(Primary Key): 테이블 내에서 각 row를 유일하게 식별하는 컬럼 + - 특징 + - 유일성: 중복된 값이 없어야 함 + - null값이 될 수 없음 + - 하나의 테이블에 한 개만 존재할 수 있음 + - 여러 컬럼을 조합해서 만들 수도 있음 (복합 PK) + + +- FK(Foreign Key): 다른 테이블의 PK를 참조하는 컬럼 + - 테이블 간의 관계를 나타내기 위해서 사용 + - 특징 + - 참조 무결성 유지: 존재하지 않는 PK 값을 FK로 가질 수 없음 + - null이나 중복 가능 + - 하나의 테이블에 여러 개 존재할 수 있음 From 3ca307d5b2c9db4a7bd73d713b9c695c29f49157 Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Fri, 16 May 2025 20:56:05 +0900 Subject: [PATCH 11/33] =?UTF-8?q?feat:=20member=20crud=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/dto/MemberController.java | 35 +++++++++++++++++ .../dto/request/CreateMemberRequest.java | 14 +++++++ .../dto/request/UpdateMemberRequest.java | 9 +++++ .../dto/response/MemberResponse.java | 17 ++++++++ .../hyunjincrudapi/member/entity/Member.java | 38 ++++++++++++++++++ .../repository/JpaMemberRepository.java | 8 ++++ .../member/service/MemberService.java | 39 +++++++++++++++++++ 7 files changed, 160 insertions(+) create mode 100644 week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/MemberController.java create mode 100644 week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/request/CreateMemberRequest.java create mode 100644 week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/request/UpdateMemberRequest.java create mode 100644 week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/response/MemberResponse.java create mode 100644 week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/entity/Member.java create mode 100644 week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/repository/JpaMemberRepository.java create mode 100644 week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/service/MemberService.java diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/MemberController.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/MemberController.java new file mode 100644 index 0000000..0435a2a --- /dev/null +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/MemberController.java @@ -0,0 +1,35 @@ +package practice.hyunjincrudapi.member.controller.dto; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; +import practice.hyunjincrudapi.member.controller.dto.request.CreateMemberRequest; +import practice.hyunjincrudapi.member.controller.dto.request.UpdateMemberRequest; +import practice.hyunjincrudapi.member.controller.dto.response.MemberResponse; +import practice.hyunjincrudapi.member.service.MemberService; + +@RestController +@RequiredArgsConstructor +@RequestMapping +public class MemberController { + private final MemberService memberService; + + @PostMapping("/members") + public void signup(@RequestBody CreateMemberRequest createMemberRequest) { + memberService.signup(createMemberRequest); + } + + @GetMapping("/members/{id}") + public MemberResponse getMember(@PathVariable Long id) { + return memberService.getMember(id); + } + + @PutMapping("/members/{id}") + public void updateMember(@PathVariable Long id, @RequestBody UpdateMemberRequest updateMemberRequest) { + memberService.updateMember(id, updateMemberRequest); + } + + @DeleteMapping("/members/{id}") + public void deleteMember(@PathVariable Long id) { + memberService.deleteMember(id); + } +} diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/request/CreateMemberRequest.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/request/CreateMemberRequest.java new file mode 100644 index 0000000..2bc2d59 --- /dev/null +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/request/CreateMemberRequest.java @@ -0,0 +1,14 @@ +package practice.hyunjincrudapi.member.controller.dto.request; + +import lombok.Getter; +import practice.hyunjincrudapi.member.entity.Member; +@Getter +public class CreateMemberRequest { + private String name; + private String password; + private String email; + + public Member toEntity(){ //toEntity 메서드를 통해 새로운 객체를 생성하게 됨 + return Member.builder().name(name).password(password).email(email).build(); + } +} diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/request/UpdateMemberRequest.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/request/UpdateMemberRequest.java new file mode 100644 index 0000000..c7cf87b --- /dev/null +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/request/UpdateMemberRequest.java @@ -0,0 +1,9 @@ +package practice.hyunjincrudapi.member.controller.dto.request; + +import lombok.Getter; + +@Getter +public class UpdateMemberRequest { + private String name; + private String email; +} diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/response/MemberResponse.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/response/MemberResponse.java new file mode 100644 index 0000000..a013d3e --- /dev/null +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/response/MemberResponse.java @@ -0,0 +1,17 @@ +package practice.hyunjincrudapi.member.controller.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import practice.hyunjincrudapi.member.entity.Member; + +@Getter +@AllArgsConstructor +public class MemberResponse { + private Long id; + private String name; + private String email; + + public static MemberResponse from(Member member) { + return new MemberResponse(member.getId(), member.getName(), member.getEmail()); + } +} diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/entity/Member.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/entity/Member.java new file mode 100644 index 0000000..3c0e349 --- /dev/null +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/entity/Member.java @@ -0,0 +1,38 @@ +package practice.hyunjincrudapi.member.entity; + +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + + +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +public class Member { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "member_id") + private Long id; + private String name; + private String password; + @Column(unique = true, nullable = false) //unique=true -> unique한 값으로 지정 + private String email; + + @Builder + public Member(String name, String password, String email) { + this.name = name; + this.password = password; + this.email = email; + } + + public void update(String name, String email){ + if(name!=null){ + this.name = name; + } + if(email!=null){ + this.email = email; + } + } +} diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/repository/JpaMemberRepository.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/repository/JpaMemberRepository.java new file mode 100644 index 0000000..838e367 --- /dev/null +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/repository/JpaMemberRepository.java @@ -0,0 +1,8 @@ +package practice.hyunjincrudapi.member.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import practice.hyunjincrudapi.member.entity.Member; + +public interface JpaMemberRepository extends JpaRepository { + +} diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/service/MemberService.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/service/MemberService.java new file mode 100644 index 0000000..9c218ee --- /dev/null +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/service/MemberService.java @@ -0,0 +1,39 @@ +package practice.hyunjincrudapi.member.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import practice.hyunjincrudapi.member.controller.dto.request.CreateMemberRequest; +import practice.hyunjincrudapi.member.controller.dto.request.UpdateMemberRequest; +import practice.hyunjincrudapi.member.controller.dto.response.MemberResponse; +import practice.hyunjincrudapi.member.entity.Member; +import practice.hyunjincrudapi.member.repository.JpaMemberRepository; + +@Service +@RequiredArgsConstructor +@Transactional +public class MemberService { + + private final JpaMemberRepository jpaMemberRepository; + + public void signup(CreateMemberRequest createMemberRequest) { + Member member = createMemberRequest.toEntity(); + jpaMemberRepository.save(member); //member에 대한 정보가 db에 저장됨 + } + + public MemberResponse getMember(Long id){ + Member member = jpaMemberRepository.findById(id).orElseThrow(()-> new IllegalArgumentException(("존재하지 않는 멤버입니다."))); + + return MemberResponse.from(member); + } + + public void updateMember(Long id, UpdateMemberRequest updateMemberRequest) { + Member member= jpaMemberRepository.findById(id).orElseThrow(()-> new IllegalArgumentException("존재하지 않는 회원입니다.")); + member.update(updateMemberRequest.getName(), updateMemberRequest.getEmail()); + } + + public void deleteMember(Long id) { + Member member = jpaMemberRepository.findById(id).orElseThrow(()-> new IllegalArgumentException("존재하지 않는 회원입니다.")); + jpaMemberRepository.delete(member); + } +} From 4ecdf12d090a33670e228a1011760a52e6fa0c54 Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Fri, 16 May 2025 20:56:19 +0900 Subject: [PATCH 12/33] =?UTF-8?q?feat:=20post=20crud=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../post/controller/dto/PostController.java | 42 +++++++++++++++ .../dto/request/CreatePostRequest.java | 13 +++++ .../dto/request/UpdatePostRequest.java | 9 ++++ .../controller/dto/response/PostResponse.java | 23 +++++++++ .../hyunjincrudapi/post/entity/Post.java | 41 +++++++++++++++ .../post/repository/JpaPostRepository.java | 8 +++ .../post/service/PostService.java | 51 +++++++++++++++++++ 7 files changed, 187 insertions(+) create mode 100644 week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/PostController.java create mode 100644 week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/request/CreatePostRequest.java create mode 100644 week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/request/UpdatePostRequest.java create mode 100644 week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/response/PostResponse.java create mode 100644 week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/entity/Post.java create mode 100644 week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/repository/JpaPostRepository.java create mode 100644 week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/service/PostService.java diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/PostController.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/PostController.java new file mode 100644 index 0000000..bca1074 --- /dev/null +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/PostController.java @@ -0,0 +1,42 @@ +package practice.hyunjincrudapi.post.controller.dto; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; +import practice.hyunjincrudapi.post.controller.dto.request.CreatePostRequest; +import practice.hyunjincrudapi.post.controller.dto.request.UpdatePostRequest; +import practice.hyunjincrudapi.post.controller.dto.response.PostResponse; +import practice.hyunjincrudapi.post.service.PostService; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping +public class PostController { + private final PostService postService; + + @PostMapping("/posts") + public void uploadPost(@RequestBody CreatePostRequest createPostRequest) { + postService.createPost(createPostRequest); + } + + @GetMapping("/posts") + public List getAllPosts(){ + return postService.getAllPosts(); + } + + @GetMapping("/posts/{id}") + public PostResponse getPostById(@PathVariable Long id) { + return postService.getPostById(id); + } + + @PutMapping("/posts/{id}") + public void updatePost(@PathVariable Long id, @RequestBody UpdatePostRequest updatePostRequest) { + postService.updatePost(id, updatePostRequest); + } + + @DeleteMapping("/posts/{id}") + public void deletePost(@PathVariable Long id) { + postService.deletePost(id); + } +} diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/request/CreatePostRequest.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/request/CreatePostRequest.java new file mode 100644 index 0000000..3b4edb2 --- /dev/null +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/request/CreatePostRequest.java @@ -0,0 +1,13 @@ +package practice.hyunjincrudapi.post.controller.dto.request; + +import lombok.Getter; +import practice.hyunjincrudapi.post.entity.Post; + +@Getter +public class CreatePostRequest { + private String title; + private String content; + private Long memberId; + + public Post toEntity() {return Post.builder().title(title).content(content).memberId(memberId).build();} +} diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/request/UpdatePostRequest.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/request/UpdatePostRequest.java new file mode 100644 index 0000000..766292f --- /dev/null +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/request/UpdatePostRequest.java @@ -0,0 +1,9 @@ +package practice.hyunjincrudapi.post.controller.dto.request; + +import lombok.Getter; + +@Getter +public class UpdatePostRequest { + private String title; + private String content; +} diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/response/PostResponse.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/response/PostResponse.java new file mode 100644 index 0000000..46532a9 --- /dev/null +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/response/PostResponse.java @@ -0,0 +1,23 @@ +package practice.hyunjincrudapi.post.controller.dto.response; + +import lombok.Getter; +import practice.hyunjincrudapi.post.entity.Post; + +@Getter +public class PostResponse { + private Long id; + private String title; + private String content; + private final Long memberId; + + public PostResponse(Long id, String title, String content, Long memberId) { + this.id = id; + this.title = title; + this.content = content; + this.memberId = memberId; + } + + public static PostResponse from(Post post) { + return new PostResponse(post.getId(), post.getTitle(), post.getContent(), post.getMemberId()); + } +} diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/entity/Post.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/entity/Post.java new file mode 100644 index 0000000..c3d5d55 --- /dev/null +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/entity/Post.java @@ -0,0 +1,41 @@ +package practice.hyunjincrudapi.post.entity; + +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +public class Post { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name="post_id") + + private Long id; + private String title; + private String content; + + @Column(name="member_id") + private Long memberId; + + @Builder + public Post(String title, String content, Long memberId) { + this.title = title; + this.content = content; + this.memberId = memberId; + } + + public void update(String title, String content) { + if(title!=null){ + this.title = title; + } + + if(content!=null){ + this.content = content; + } + } + +} diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/repository/JpaPostRepository.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/repository/JpaPostRepository.java new file mode 100644 index 0000000..076910a --- /dev/null +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/repository/JpaPostRepository.java @@ -0,0 +1,8 @@ +package practice.hyunjincrudapi.post.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import practice.hyunjincrudapi.post.entity.Post; + +public interface JpaPostRepository extends JpaRepository { + +} diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/service/PostService.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/service/PostService.java new file mode 100644 index 0000000..c5601df --- /dev/null +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/service/PostService.java @@ -0,0 +1,51 @@ +package practice.hyunjincrudapi.post.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import practice.hyunjincrudapi.member.repository.JpaMemberRepository; +import practice.hyunjincrudapi.post.controller.dto.request.CreatePostRequest; +import practice.hyunjincrudapi.post.controller.dto.request.UpdatePostRequest; +import practice.hyunjincrudapi.post.controller.dto.response.PostResponse; +import practice.hyunjincrudapi.post.entity.Post; +import practice.hyunjincrudapi.post.repository.JpaPostRepository; + +import java.util.List; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +@Transactional +public class PostService { + private final JpaPostRepository postRepository; + private final JpaMemberRepository memberRepository; + + public void createPost(CreatePostRequest request) { + if (!memberRepository.existsById(request.getMemberId())) { + throw new IllegalArgumentException("존재하지 않는 회원입니다."); + } + + postRepository.save(request.toEntity()); + } + + public List getAllPosts(){ + return postRepository.findAll().stream().map(PostResponse::from).collect(Collectors.toList()); + } + + public PostResponse getPostById(Long id) { + return postRepository.findById(id) + .map(PostResponse::from) + .orElseThrow(() -> new IllegalArgumentException("해당 게시글이 존재하지 않습니다.")); + } + + public void updatePost(Long id, UpdatePostRequest request) { + Post post = postRepository.findById(id).orElseThrow(()-> new IllegalArgumentException(("해당 게시글이 존재하지 않습니다."))); + post.update(request.getTitle(), request.getContent()); + } + + public void deletePost(Long id) { + Post post = postRepository.findById(id).orElseThrow(()-> new IllegalArgumentException(("해당 게시글이 존재하지 않습니다."))); + postRepository.delete(post); + } +} + From a96cfe2c9a64df66813af916fc4506c11ca37085 Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Fri, 16 May 2025 21:08:39 +0900 Subject: [PATCH 13/33] =?UTF-8?q?refactor:=20Post=20-=20Member=20ManyToOne?= =?UTF-8?q?=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/dto/request/CreatePostRequest.java | 2 -- .../post/controller/dto/response/PostResponse.java | 4 ++-- .../practice/hyunjincrudapi/post/entity/Post.java | 10 ++++++---- .../hyunjincrudapi/post/service/PostService.java | 14 ++++++++++---- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/request/CreatePostRequest.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/request/CreatePostRequest.java index 3b4edb2..6da8302 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/request/CreatePostRequest.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/request/CreatePostRequest.java @@ -8,6 +8,4 @@ public class CreatePostRequest { private String title; private String content; private Long memberId; - - public Post toEntity() {return Post.builder().title(title).content(content).memberId(memberId).build();} } diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/response/PostResponse.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/response/PostResponse.java index 46532a9..74c32f3 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/response/PostResponse.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/response/PostResponse.java @@ -8,7 +8,7 @@ public class PostResponse { private Long id; private String title; private String content; - private final Long memberId; + private Long memberId; public PostResponse(Long id, String title, String content, Long memberId) { this.id = id; @@ -18,6 +18,6 @@ public PostResponse(Long id, String title, String content, Long memberId) { } public static PostResponse from(Post post) { - return new PostResponse(post.getId(), post.getTitle(), post.getContent(), post.getMemberId()); + return new PostResponse(post.getId(), post.getTitle(), post.getContent(), post.getMember().getId()); } } diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/entity/Post.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/entity/Post.java index c3d5d55..c4213ae 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/entity/Post.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/entity/Post.java @@ -5,6 +5,7 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import practice.hyunjincrudapi.member.entity.Member; @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -18,14 +19,15 @@ public class Post { private String title; private String content; - @Column(name="member_id") - private Long memberId; + @ManyToOne + @JoinColumn(name = "member_id", nullable = false) + private Member member; @Builder - public Post(String title, String content, Long memberId) { + public Post(String title, String content, Member member) { this.title = title; this.content = content; - this.memberId = memberId; + this.member = member; } public void update(String title, String content) { diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/service/PostService.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/service/PostService.java index c5601df..6efc97c 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/service/PostService.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/service/PostService.java @@ -3,6 +3,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import practice.hyunjincrudapi.member.entity.Member; import practice.hyunjincrudapi.member.repository.JpaMemberRepository; import practice.hyunjincrudapi.post.controller.dto.request.CreatePostRequest; import practice.hyunjincrudapi.post.controller.dto.request.UpdatePostRequest; @@ -21,11 +22,16 @@ public class PostService { private final JpaMemberRepository memberRepository; public void createPost(CreatePostRequest request) { - if (!memberRepository.existsById(request.getMemberId())) { - throw new IllegalArgumentException("존재하지 않는 회원입니다."); - } + Member member = memberRepository.findById(request.getMemberId()) + .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 회원입니다.")); - postRepository.save(request.toEntity()); + Post post = Post.builder() + .title(request.getTitle()) + .content(request.getContent()) + .member(member) + .build(); + + postRepository.save(post); } public List getAllPosts(){ From f3e5d69baa479476aa02db29dc8af684d2a166b3 Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Fri, 16 May 2025 21:49:54 +0900 Subject: [PATCH 14/33] =?UTF-8?q?feat:=20comment=20crud=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/dto/CommentController.java | 37 +++++++++++++ .../dto/request/CreateCommentRequest.java | 10 ++++ .../dto/request/UpdateCommentRequest.java | 8 +++ .../dto/response/CommentResponse.java | 23 ++++++++ .../comment/entity/Comment.java | 41 ++++++++++++++ .../repository/JpaCommentRepository.java | 11 ++++ .../comment/service/CommentService.java | 54 +++++++++++++++++++ 7 files changed, 184 insertions(+) create mode 100644 week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/CommentController.java create mode 100644 week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/request/CreateCommentRequest.java create mode 100644 week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/request/UpdateCommentRequest.java create mode 100644 week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/response/CommentResponse.java create mode 100644 week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/entity/Comment.java create mode 100644 week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/repository/JpaCommentRepository.java create mode 100644 week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/service/CommentService.java diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/CommentController.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/CommentController.java new file mode 100644 index 0000000..1cc9b86 --- /dev/null +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/CommentController.java @@ -0,0 +1,37 @@ +package practice.hyunjincrudapi.comment.controller.dto; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; +import practice.hyunjincrudapi.comment.controller.dto.request.CreateCommentRequest; +import practice.hyunjincrudapi.comment.controller.dto.request.UpdateCommentRequest; +import practice.hyunjincrudapi.comment.controller.dto.response.CommentResponse; +import practice.hyunjincrudapi.comment.service.CommentService; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping +public class CommentController { + private final CommentService commentService; + + @PostMapping("/comment") + public void createComment(@RequestBody CreateCommentRequest request) { + commentService.createComment(request); + } + + @GetMapping("/post/{postId}/comment") + public List getComments(@PathVariable Long postId) { + return commentService.getCommentsByPost(postId); + } + + @PatchMapping("/comment/{id}") + public void updateComment(@PathVariable Long id, @RequestBody UpdateCommentRequest request) { + commentService.updateComment(id, request); + } + + @DeleteMapping("/comment/{id}") + public void deleteComment(@PathVariable Long id) { + commentService.deleteComment(id); + } +} diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/request/CreateCommentRequest.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/request/CreateCommentRequest.java new file mode 100644 index 0000000..13c7371 --- /dev/null +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/request/CreateCommentRequest.java @@ -0,0 +1,10 @@ +package practice.hyunjincrudapi.comment.controller.dto.request; + +import lombok.Getter; + +@Getter +public class CreateCommentRequest { + private String content; + private Long memberId; + private Long postId; +} diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/request/UpdateCommentRequest.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/request/UpdateCommentRequest.java new file mode 100644 index 0000000..406d56c --- /dev/null +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/request/UpdateCommentRequest.java @@ -0,0 +1,8 @@ +package practice.hyunjincrudapi.comment.controller.dto.request; + +import lombok.Getter; + +@Getter +public class UpdateCommentRequest { + private String content; +} diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/response/CommentResponse.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/response/CommentResponse.java new file mode 100644 index 0000000..3934962 --- /dev/null +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/response/CommentResponse.java @@ -0,0 +1,23 @@ +package practice.hyunjincrudapi.comment.controller.dto.response; + +import lombok.Getter; +import practice.hyunjincrudapi.comment.entity.Comment; + +@Getter +public class CommentResponse { + private Long id; + private String content; + private Long memberId; + private Long postId; + + public CommentResponse(Long id, String content, Long memberId, Long postId) { + this.id = id; + this.content = content; + this.memberId = memberId; + this.postId = postId; + } + + public static CommentResponse from(Comment comment) { + return new CommentResponse(comment.getId(), comment.getContent(), comment.getMember().getId(), comment.getPost().getId()); + } +} diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/entity/Comment.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/entity/Comment.java new file mode 100644 index 0000000..e33cc56 --- /dev/null +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/entity/Comment.java @@ -0,0 +1,41 @@ +package practice.hyunjincrudapi.comment.entity; + +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import practice.hyunjincrudapi.member.entity.Member; +import practice.hyunjincrudapi.post.entity.Post; + +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +public class Comment { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name="comment_id") + private Long id; + private String content; + + @ManyToOne + @JoinColumn(name = "member_id", nullable = false) + private Member member; + + @ManyToOne + @JoinColumn(name="post_id", nullable = false) + private Post post; + + @Builder + public Comment(String content, Member member, Post post) { + this.content = content; + this.member = member; + this.post = post; + } + + public void updateContent(String content) { + if(content!=null){ + this.content = content; + } + } +} diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/repository/JpaCommentRepository.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/repository/JpaCommentRepository.java new file mode 100644 index 0000000..340117d --- /dev/null +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/repository/JpaCommentRepository.java @@ -0,0 +1,11 @@ +package practice.hyunjincrudapi.comment.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import practice.hyunjincrudapi.comment.entity.Comment; + +import java.util.List; + + +public interface JpaCommentRepository extends JpaRepository { + List findByPostId(Long postId); +} diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/service/CommentService.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/service/CommentService.java new file mode 100644 index 0000000..29b605b --- /dev/null +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/service/CommentService.java @@ -0,0 +1,54 @@ +package practice.hyunjincrudapi.comment.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import practice.hyunjincrudapi.comment.controller.dto.request.CreateCommentRequest; +import practice.hyunjincrudapi.comment.controller.dto.request.UpdateCommentRequest; +import practice.hyunjincrudapi.comment.controller.dto.response.CommentResponse; +import practice.hyunjincrudapi.comment.entity.Comment; +import practice.hyunjincrudapi.comment.repository.JpaCommentRepository; +import practice.hyunjincrudapi.member.entity.Member; +import practice.hyunjincrudapi.member.repository.JpaMemberRepository; +import practice.hyunjincrudapi.post.entity.Post; +import practice.hyunjincrudapi.post.repository.JpaPostRepository; + +import java.util.List; + +@Service +@RequiredArgsConstructor +@Transactional +public class CommentService { + private final JpaCommentRepository commentRepository; + private final JpaMemberRepository memberRepository; + private final JpaPostRepository postRepository; + + public void createComment(CreateCommentRequest request) { + Member member = memberRepository.findById(request.getMemberId()).orElseThrow(() -> new IllegalArgumentException("존재하지 않는 회원입니다.")); + + Post post = postRepository.findById(request.getPostId()) + .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 게시글입니다.")); + + Comment comment = Comment.builder() + .content(request.getContent()) + .member(member) + .post(post) + .build(); + + commentRepository.save(comment); + } + + public List getCommentsByPost(Long postId){ + return commentRepository.findByPostId(postId).stream().map(CommentResponse::from).toList(); + } + + public void updateComment(Long commentId, UpdateCommentRequest request) { + Comment comment = commentRepository.findById(commentId).orElseThrow(()-> new IllegalArgumentException(("존재하지 않는 댓글입니다."))); + comment.updateContent(request.getContent()); + } + + public void deleteComment(Long commentId){ + Comment comment = commentRepository.findById(commentId).orElseThrow(()-> new IllegalArgumentException(("존재하지 않는 댓글입니다."))); + commentRepository.delete(comment); + } +} From b50e24209542e59cc0ba57fb3439de3cf6711737 Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Sat, 17 May 2025 20:44:12 +0900 Subject: [PATCH 15/33] =?UTF-8?q?refactor:=20toEntity=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20request=20dto=EC=97=90=EC=84=9C=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/dto/request/CreateCommentRequest.java | 7 +++++++ .../hyunjincrudapi/comment/service/CommentService.java | 6 +----- .../post/controller/dto/request/CreatePostRequest.java | 9 +++++++++ .../hyunjincrudapi/post/service/PostService.java | 7 +------ 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/request/CreateCommentRequest.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/request/CreateCommentRequest.java index 13c7371..cd86d31 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/request/CreateCommentRequest.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/request/CreateCommentRequest.java @@ -1,10 +1,17 @@ package practice.hyunjincrudapi.comment.controller.dto.request; import lombok.Getter; +import practice.hyunjincrudapi.comment.entity.Comment; +import practice.hyunjincrudapi.member.entity.Member; +import practice.hyunjincrudapi.post.entity.Post; @Getter public class CreateCommentRequest { private String content; private Long memberId; private Long postId; + + public Comment toEntity(Member member, Post post) { + return Comment.builder().content(content).member(member).post(post).build(); + } } diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/service/CommentService.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/service/CommentService.java index 29b605b..832cedb 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/service/CommentService.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/service/CommentService.java @@ -29,11 +29,7 @@ public void createComment(CreateCommentRequest request) { Post post = postRepository.findById(request.getPostId()) .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 게시글입니다.")); - Comment comment = Comment.builder() - .content(request.getContent()) - .member(member) - .post(post) - .build(); + Comment comment = request.toEntity(member, post); commentRepository.save(comment); } diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/request/CreatePostRequest.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/request/CreatePostRequest.java index 6da8302..3ad19d6 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/request/CreatePostRequest.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/request/CreatePostRequest.java @@ -1,6 +1,7 @@ package practice.hyunjincrudapi.post.controller.dto.request; import lombok.Getter; +import practice.hyunjincrudapi.member.entity.Member; import practice.hyunjincrudapi.post.entity.Post; @Getter @@ -8,4 +9,12 @@ public class CreatePostRequest { private String title; private String content; private Long memberId; + + public Post toEntity(Member member) { + return Post.builder() + .title(title) + .content(content) + .member(member) + .build(); + } } diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/service/PostService.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/service/PostService.java index 6efc97c..cfe8c77 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/service/PostService.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/service/PostService.java @@ -25,12 +25,7 @@ public void createPost(CreatePostRequest request) { Member member = memberRepository.findById(request.getMemberId()) .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 회원입니다.")); - Post post = Post.builder() - .title(request.getTitle()) - .content(request.getContent()) - .member(member) - .build(); - + Post post = request.toEntity(member); postRepository.save(post); } From 8488fcff0aaa7cff99493c64b4b736a400241258 Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Sat, 17 May 2025 20:49:52 +0900 Subject: [PATCH 16/33] =?UTF-8?q?refactor:=20member=EC=97=90=20OneToMany?= =?UTF-8?q?=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../practice/hyunjincrudapi/member/entity/Member.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/entity/Member.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/entity/Member.java index 3c0e349..3a0806e 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/entity/Member.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/entity/Member.java @@ -5,6 +5,11 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import practice.hyunjincrudapi.comment.entity.Comment; +import practice.hyunjincrudapi.post.entity.Post; + +import java.util.ArrayList; +import java.util.List; @Entity @@ -20,6 +25,12 @@ public class Member { @Column(unique = true, nullable = false) //unique=true -> unique한 값으로 지정 private String email; + @OneToMany(mappedBy = "member", cascade = CascadeType.ALL) + private List posts = new ArrayList<>(); + + @OneToMany(mappedBy = "member", cascade = CascadeType.ALL) + private List comments = new ArrayList<>(); + @Builder public Member(String name, String password, String email) { this.name = name; From ea02ef326f0e7f09d9f843d65cccfdc41b142fdd Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Sat, 17 May 2025 20:51:37 +0900 Subject: [PATCH 17/33] =?UTF-8?q?refactor:=20post=EC=97=90=20OneToMany=20?= =?UTF-8?q?=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/practice/hyunjincrudapi/post/entity/Post.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/entity/Post.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/entity/Post.java index c4213ae..617db54 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/entity/Post.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/entity/Post.java @@ -5,8 +5,12 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import practice.hyunjincrudapi.comment.entity.Comment; import practice.hyunjincrudapi.member.entity.Member; +import java.util.ArrayList; +import java.util.List; + @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter @@ -23,6 +27,9 @@ public class Post { @JoinColumn(name = "member_id", nullable = false) private Member member; + @OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true) + private List comments = new ArrayList<>(); + @Builder public Post(String title, String content, Member member) { this.title = title; From 2764cd0f0fd8fbe8e782c88117cd092dfa16b344 Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Sat, 17 May 2025 20:58:43 +0900 Subject: [PATCH 18/33] =?UTF-8?q?refactor:=20comment=20=EC=97=94=EB=93=9C?= =?UTF-8?q?=ED=8F=AC=EC=9D=B8=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../comment/controller/dto/CommentController.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/CommentController.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/CommentController.java index 1cc9b86..e518b2b 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/CommentController.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/CommentController.java @@ -15,22 +15,22 @@ public class CommentController { private final CommentService commentService; - @PostMapping("/comment") + @PostMapping("/comments") public void createComment(@RequestBody CreateCommentRequest request) { commentService.createComment(request); } - @GetMapping("/post/{postId}/comment") + @GetMapping("/posts/{postId}/comments") public List getComments(@PathVariable Long postId) { return commentService.getCommentsByPost(postId); } - @PatchMapping("/comment/{id}") + @PatchMapping("/comments/{id}") public void updateComment(@PathVariable Long id, @RequestBody UpdateCommentRequest request) { commentService.updateComment(id, request); } - @DeleteMapping("/comment/{id}") + @DeleteMapping("/comments/{id}") public void deleteComment(@PathVariable Long id) { commentService.deleteComment(id); } From c9d40b20abb6481a7289cb13c6fdb6c1e65d0172 Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Mon, 19 May 2025 14:03:07 +0900 Subject: [PATCH 19/33] =?UTF-8?q?refactor:=20=EC=A4=84=EB=B0=94=EA=BF=88?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/request/CreateCommentRequest.java | 6 +++++- .../comment/service/CommentService.java | 18 ++++++++++++++---- .../dto/request/CreateMemberRequest.java | 7 ++++++- .../member/service/MemberService.java | 14 +++++++++++--- .../hyunjincrudapi/post/entity/Post.java | 1 - .../post/service/PostService.java | 14 +++++++++++--- 6 files changed, 47 insertions(+), 13 deletions(-) diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/request/CreateCommentRequest.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/request/CreateCommentRequest.java index cd86d31..adaeb80 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/request/CreateCommentRequest.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/request/CreateCommentRequest.java @@ -12,6 +12,10 @@ public class CreateCommentRequest { private Long postId; public Comment toEntity(Member member, Post post) { - return Comment.builder().content(content).member(member).post(post).build(); + return Comment.builder() + .content(content) + .member(member) + .post(post) + .build(); } } diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/service/CommentService.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/service/CommentService.java index 832cedb..b4e5bef 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/service/CommentService.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/service/CommentService.java @@ -24,7 +24,9 @@ public class CommentService { private final JpaPostRepository postRepository; public void createComment(CreateCommentRequest request) { - Member member = memberRepository.findById(request.getMemberId()).orElseThrow(() -> new IllegalArgumentException("존재하지 않는 회원입니다.")); + Member member = memberRepository + .findById(request.getMemberId()) + .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 회원입니다.")); Post post = postRepository.findById(request.getPostId()) .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 게시글입니다.")); @@ -35,16 +37,24 @@ public void createComment(CreateCommentRequest request) { } public List getCommentsByPost(Long postId){ - return commentRepository.findByPostId(postId).stream().map(CommentResponse::from).toList(); + return commentRepository.findByPostId(postId).stream() + .map(CommentResponse::from) + .toList(); } public void updateComment(Long commentId, UpdateCommentRequest request) { - Comment comment = commentRepository.findById(commentId).orElseThrow(()-> new IllegalArgumentException(("존재하지 않는 댓글입니다."))); + Comment comment = commentRepository + .findById(commentId) + .orElseThrow(()-> new IllegalArgumentException(("존재하지 않는 댓글입니다."))); + comment.updateContent(request.getContent()); } public void deleteComment(Long commentId){ - Comment comment = commentRepository.findById(commentId).orElseThrow(()-> new IllegalArgumentException(("존재하지 않는 댓글입니다."))); + Comment comment = commentRepository + .findById(commentId) + .orElseThrow(()-> new IllegalArgumentException(("존재하지 않는 댓글입니다."))); + commentRepository.delete(comment); } } diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/request/CreateMemberRequest.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/request/CreateMemberRequest.java index 2bc2d59..54fa6e1 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/request/CreateMemberRequest.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/request/CreateMemberRequest.java @@ -9,6 +9,11 @@ public class CreateMemberRequest { private String email; public Member toEntity(){ //toEntity 메서드를 통해 새로운 객체를 생성하게 됨 - return Member.builder().name(name).password(password).email(email).build(); + return Member + .builder() + .name(name) + .password(password) + .email(email) + .build(); } } diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/service/MemberService.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/service/MemberService.java index 9c218ee..cf5e8aa 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/service/MemberService.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/service/MemberService.java @@ -22,18 +22,26 @@ public void signup(CreateMemberRequest createMemberRequest) { } public MemberResponse getMember(Long id){ - Member member = jpaMemberRepository.findById(id).orElseThrow(()-> new IllegalArgumentException(("존재하지 않는 멤버입니다."))); + Member member = jpaMemberRepository + .findById(id) + .orElseThrow(()-> new IllegalArgumentException(("존재하지 않는 멤버입니다."))); return MemberResponse.from(member); } public void updateMember(Long id, UpdateMemberRequest updateMemberRequest) { - Member member= jpaMemberRepository.findById(id).orElseThrow(()-> new IllegalArgumentException("존재하지 않는 회원입니다.")); + Member member= jpaMemberRepository + .findById(id) + .orElseThrow(()-> new IllegalArgumentException("존재하지 않는 회원입니다.")); + member.update(updateMemberRequest.getName(), updateMemberRequest.getEmail()); } public void deleteMember(Long id) { - Member member = jpaMemberRepository.findById(id).orElseThrow(()-> new IllegalArgumentException("존재하지 않는 회원입니다.")); + Member member = jpaMemberRepository + .findById(id) + .orElseThrow(()-> new IllegalArgumentException("존재하지 않는 회원입니다.")); + jpaMemberRepository.delete(member); } } diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/entity/Post.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/entity/Post.java index 617db54..c0d199f 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/entity/Post.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/entity/Post.java @@ -18,7 +18,6 @@ public class Post { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name="post_id") - private Long id; private String title; private String content; diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/service/PostService.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/service/PostService.java index cfe8c77..eabb4ee 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/service/PostService.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/service/PostService.java @@ -30,7 +30,9 @@ public void createPost(CreatePostRequest request) { } public List getAllPosts(){ - return postRepository.findAll().stream().map(PostResponse::from).collect(Collectors.toList()); + return postRepository.findAll().stream() + .map(PostResponse::from) + .collect(Collectors.toList()); } public PostResponse getPostById(Long id) { @@ -40,12 +42,18 @@ public PostResponse getPostById(Long id) { } public void updatePost(Long id, UpdatePostRequest request) { - Post post = postRepository.findById(id).orElseThrow(()-> new IllegalArgumentException(("해당 게시글이 존재하지 않습니다."))); + Post post = postRepository + .findById(id) + .orElseThrow(()-> new IllegalArgumentException(("해당 게시글이 존재하지 않습니다."))); + post.update(request.getTitle(), request.getContent()); } public void deletePost(Long id) { - Post post = postRepository.findById(id).orElseThrow(()-> new IllegalArgumentException(("해당 게시글이 존재하지 않습니다."))); + Post post = postRepository + .findById(id) + .orElseThrow(()-> new IllegalArgumentException(("해당 게시글이 존재하지 않습니다."))); + postRepository.delete(post); } } From 336c6d8ab71332e0e3b7c570c359e00149563a44 Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Mon, 19 May 2025 14:11:11 +0900 Subject: [PATCH 20/33] =?UTF-8?q?refactor:=20@RequestMapping=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../comment/controller/dto/CommentController.java | 10 +++++----- .../member/controller/dto/MemberController.java | 10 +++++----- .../post/controller/dto/PostController.java | 12 ++++++------ 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/CommentController.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/CommentController.java index e518b2b..ad736c0 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/CommentController.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/CommentController.java @@ -11,26 +11,26 @@ @RestController @RequiredArgsConstructor -@RequestMapping +@RequestMapping("/comments") public class CommentController { private final CommentService commentService; - @PostMapping("/comments") + @PostMapping public void createComment(@RequestBody CreateCommentRequest request) { commentService.createComment(request); } - @GetMapping("/posts/{postId}/comments") + @GetMapping("/posts/{postId}") public List getComments(@PathVariable Long postId) { return commentService.getCommentsByPost(postId); } - @PatchMapping("/comments/{id}") + @PatchMapping("/{id}") public void updateComment(@PathVariable Long id, @RequestBody UpdateCommentRequest request) { commentService.updateComment(id, request); } - @DeleteMapping("/comments/{id}") + @DeleteMapping("/{id}") public void deleteComment(@PathVariable Long id) { commentService.deleteComment(id); } diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/MemberController.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/MemberController.java index 0435a2a..a8f72f3 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/MemberController.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/MemberController.java @@ -9,26 +9,26 @@ @RestController @RequiredArgsConstructor -@RequestMapping +@RequestMapping("/members") public class MemberController { private final MemberService memberService; - @PostMapping("/members") + @PostMapping public void signup(@RequestBody CreateMemberRequest createMemberRequest) { memberService.signup(createMemberRequest); } - @GetMapping("/members/{id}") + @GetMapping("/{id}") public MemberResponse getMember(@PathVariable Long id) { return memberService.getMember(id); } - @PutMapping("/members/{id}") + @PutMapping("/{id}") public void updateMember(@PathVariable Long id, @RequestBody UpdateMemberRequest updateMemberRequest) { memberService.updateMember(id, updateMemberRequest); } - @DeleteMapping("/members/{id}") + @DeleteMapping("/{id}") public void deleteMember(@PathVariable Long id) { memberService.deleteMember(id); } diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/PostController.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/PostController.java index bca1074..5a07de8 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/PostController.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/PostController.java @@ -11,31 +11,31 @@ @RestController @RequiredArgsConstructor -@RequestMapping +@RequestMapping("/posts") public class PostController { private final PostService postService; - @PostMapping("/posts") + @PostMapping public void uploadPost(@RequestBody CreatePostRequest createPostRequest) { postService.createPost(createPostRequest); } - @GetMapping("/posts") + @GetMapping public List getAllPosts(){ return postService.getAllPosts(); } - @GetMapping("/posts/{id}") + @GetMapping("/{id}") public PostResponse getPostById(@PathVariable Long id) { return postService.getPostById(id); } - @PutMapping("/posts/{id}") + @PutMapping("/{id}") public void updatePost(@PathVariable Long id, @RequestBody UpdatePostRequest updatePostRequest) { postService.updatePost(id, updatePostRequest); } - @DeleteMapping("/posts/{id}") + @DeleteMapping("/{id}") public void deletePost(@PathVariable Long id) { postService.deletePost(id); } From 89124e58e455e86348879258cb14da51f13c6687 Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Mon, 19 May 2025 14:23:43 +0900 Subject: [PATCH 21/33] =?UTF-8?q?refactor:=20controller=20=EB=82=B4?= =?UTF-8?q?=EB=B6=80=20=EB=A9=94=EC=84=9C=EB=93=9C=EB=AA=85=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../comment/controller/dto/CommentController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/CommentController.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/CommentController.java index ad736c0..e08bd15 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/CommentController.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/CommentController.java @@ -21,7 +21,7 @@ public void createComment(@RequestBody CreateCommentRequest request) { } @GetMapping("/posts/{postId}") - public List getComments(@PathVariable Long postId) { + public List getCommentsByPost(@PathVariable Long postId) { return commentService.getCommentsByPost(postId); } From 6a419c0b4def5dd9d524643d56b60aa492ca0852 Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Mon, 19 May 2025 14:26:52 +0900 Subject: [PATCH 22/33] =?UTF-8?q?refactor:=20@AllArgsConstructor=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../comment/controller/dto/response/CommentResponse.java | 9 ++------- .../post/controller/dto/response/PostResponse.java | 9 ++------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/response/CommentResponse.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/response/CommentResponse.java index 3934962..05d5fdd 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/response/CommentResponse.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/response/CommentResponse.java @@ -1,22 +1,17 @@ package practice.hyunjincrudapi.comment.controller.dto.response; +import lombok.AllArgsConstructor; import lombok.Getter; import practice.hyunjincrudapi.comment.entity.Comment; @Getter +@AllArgsConstructor public class CommentResponse { private Long id; private String content; private Long memberId; private Long postId; - public CommentResponse(Long id, String content, Long memberId, Long postId) { - this.id = id; - this.content = content; - this.memberId = memberId; - this.postId = postId; - } - public static CommentResponse from(Comment comment) { return new CommentResponse(comment.getId(), comment.getContent(), comment.getMember().getId(), comment.getPost().getId()); } diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/response/PostResponse.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/response/PostResponse.java index 74c32f3..23631de 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/response/PostResponse.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/response/PostResponse.java @@ -1,22 +1,17 @@ package practice.hyunjincrudapi.post.controller.dto.response; +import lombok.AllArgsConstructor; import lombok.Getter; import practice.hyunjincrudapi.post.entity.Post; @Getter +@AllArgsConstructor public class PostResponse { private Long id; private String title; private String content; private Long memberId; - public PostResponse(Long id, String title, String content, Long memberId) { - this.id = id; - this.title = title; - this.content = content; - this.memberId = memberId; - } - public static PostResponse from(Post post) { return new PostResponse(post.getId(), post.getTitle(), post.getContent(), post.getMember().getId()); } From 120d959ec3aaaf8fd0219c044048b802abe8fe89 Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Mon, 19 May 2025 21:59:09 +0900 Subject: [PATCH 23/33] =?UTF-8?q?refactor:=20comment=20=EC=97=94=EB=93=9C?= =?UTF-8?q?=ED=8F=AC=EC=9D=B8=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../comment/controller/dto/CommentController.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/CommentController.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/CommentController.java index e08bd15..6b52ec3 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/CommentController.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/CommentController.java @@ -11,26 +11,25 @@ @RestController @RequiredArgsConstructor -@RequestMapping("/comments") public class CommentController { private final CommentService commentService; - @PostMapping + @PostMapping("/comments") public void createComment(@RequestBody CreateCommentRequest request) { commentService.createComment(request); } - @GetMapping("/posts/{postId}") + @GetMapping("/posts/{postId}/comments") public List getCommentsByPost(@PathVariable Long postId) { return commentService.getCommentsByPost(postId); } - @PatchMapping("/{id}") + @PatchMapping("/comments/{id}") public void updateComment(@PathVariable Long id, @RequestBody UpdateCommentRequest request) { commentService.updateComment(id, request); } - @DeleteMapping("/{id}") + @DeleteMapping("/comments/{id}") public void deleteComment(@PathVariable Long id) { commentService.deleteComment(id); } From 6bdb74777a05868b46a05e9fd8b1dc1474e0e371 Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Tue, 20 May 2025 20:11:36 +0900 Subject: [PATCH 24/33] =?UTF-8?q?refactor:=20null=20=EA=B2=80=EC=A6=9D=20d?= =?UTF-8?q?to=EC=97=90=EC=84=9C=20=EC=88=98=ED=96=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../comment/controller/dto/CommentController.java | 3 ++- .../comment/controller/dto/request/UpdateCommentRequest.java | 2 ++ .../hyunjincrudapi/post/controller/dto/PostController.java | 3 ++- .../post/controller/dto/request/UpdatePostRequest.java | 3 +++ 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/CommentController.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/CommentController.java index 6b52ec3..f1c5af5 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/CommentController.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/CommentController.java @@ -1,5 +1,6 @@ package practice.hyunjincrudapi.comment.controller.dto; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; import practice.hyunjincrudapi.comment.controller.dto.request.CreateCommentRequest; @@ -25,7 +26,7 @@ public List getCommentsByPost(@PathVariable Long postId) { } @PatchMapping("/comments/{id}") - public void updateComment(@PathVariable Long id, @RequestBody UpdateCommentRequest request) { + public void updateComment(@PathVariable Long id, @RequestBody @Valid UpdateCommentRequest request) { commentService.updateComment(id, request); } diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/request/UpdateCommentRequest.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/request/UpdateCommentRequest.java index 406d56c..769b29e 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/request/UpdateCommentRequest.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/request/UpdateCommentRequest.java @@ -1,8 +1,10 @@ package practice.hyunjincrudapi.comment.controller.dto.request; +import jakarta.validation.constraints.NotBlank; import lombok.Getter; @Getter public class UpdateCommentRequest { + @NotBlank(message = "댓글 내용은 비어 있을 수 없습니다.") private String content; } diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/PostController.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/PostController.java index 5a07de8..0026125 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/PostController.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/PostController.java @@ -1,5 +1,6 @@ package practice.hyunjincrudapi.post.controller.dto; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; import practice.hyunjincrudapi.post.controller.dto.request.CreatePostRequest; @@ -31,7 +32,7 @@ public PostResponse getPostById(@PathVariable Long id) { } @PutMapping("/{id}") - public void updatePost(@PathVariable Long id, @RequestBody UpdatePostRequest updatePostRequest) { + public void updatePost(@PathVariable Long id, @RequestBody @Valid UpdatePostRequest updatePostRequest) { postService.updatePost(id, updatePostRequest); } diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/request/UpdatePostRequest.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/request/UpdatePostRequest.java index 766292f..2ffb931 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/request/UpdatePostRequest.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/request/UpdatePostRequest.java @@ -1,9 +1,12 @@ package practice.hyunjincrudapi.post.controller.dto.request; +import jakarta.validation.constraints.NotBlank; import lombok.Getter; @Getter public class UpdatePostRequest { + @NotBlank(message = "제목을 입력해주세요.") private String title; + @NotBlank(message = "내용을 입력해주세요.") private String content; } From d4a7ea049e8b6a0246d3b0b88a2eb3ff68ea20c6 Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Tue, 20 May 2025 20:11:57 +0900 Subject: [PATCH 25/33] =?UTF-8?q?refactor:=20post=20update=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/practice/hyunjincrudapi/post/entity/Post.java | 2 +- .../java/practice/hyunjincrudapi/post/service/PostService.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/entity/Post.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/entity/Post.java index c0d199f..3983bc8 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/entity/Post.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/entity/Post.java @@ -36,7 +36,7 @@ public Post(String title, String content, Member member) { this.member = member; } - public void update(String title, String content) { + public void updatePost(String title, String content) { if(title!=null){ this.title = title; } diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/service/PostService.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/service/PostService.java index eabb4ee..331e52e 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/service/PostService.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/service/PostService.java @@ -46,7 +46,7 @@ public void updatePost(Long id, UpdatePostRequest request) { .findById(id) .orElseThrow(()-> new IllegalArgumentException(("해당 게시글이 존재하지 않습니다."))); - post.update(request.getTitle(), request.getContent()); + post.updatePost(request.getTitle(), request.getContent()); } public void deletePost(Long id) { From 8e52430931e575e02bf527fff0bf973f32dafe19 Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Tue, 20 May 2025 20:22:10 +0900 Subject: [PATCH 26/33] =?UTF-8?q?refactor:=20dto=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=20=EC=9C=84=EC=B9=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../comment/controller/{dto => }/CommentController.java | 8 ++++---- .../dto/request/CreateCommentRequest.java | 2 +- .../dto/request/UpdateCommentRequest.java | 2 +- .../{controller => }/dto/response/CommentResponse.java | 2 +- .../hyunjincrudapi/comment/service/CommentService.java | 6 +++--- .../member/controller/{dto => }/MemberController.java | 8 ++++---- .../{controller => }/dto/request/CreateMemberRequest.java | 2 +- .../{controller => }/dto/request/UpdateMemberRequest.java | 2 +- .../{controller => }/dto/response/MemberResponse.java | 2 +- .../hyunjincrudapi/member/service/MemberService.java | 6 +++--- .../post/controller/{dto => }/PostController.java | 8 ++++---- .../{controller => }/dto/request/CreatePostRequest.java | 2 +- .../{controller => }/dto/request/UpdatePostRequest.java | 2 +- .../post/{controller => }/dto/response/PostResponse.java | 2 +- .../practice/hyunjincrudapi/post/service/PostService.java | 6 +++--- 15 files changed, 30 insertions(+), 30 deletions(-) rename week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/{dto => }/CommentController.java (77%) rename week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/{controller => }/dto/request/CreateCommentRequest.java (89%) rename week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/{controller => }/dto/request/UpdateCommentRequest.java (77%) rename week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/{controller => }/dto/response/CommentResponse.java (87%) rename week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/{dto => }/MemberController.java (75%) rename week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/{controller => }/dto/request/CreateMemberRequest.java (88%) rename week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/{controller => }/dto/request/UpdateMemberRequest.java (65%) rename week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/{controller => }/dto/response/MemberResponse.java (85%) rename week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/{dto => }/PostController.java (79%) rename week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/{controller => }/dto/request/CreatePostRequest.java (88%) rename week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/{controller => }/dto/request/UpdatePostRequest.java (82%) rename week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/{controller => }/dto/response/PostResponse.java (87%) diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/CommentController.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/CommentController.java similarity index 77% rename from week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/CommentController.java rename to week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/CommentController.java index f1c5af5..96e7eed 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/CommentController.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/CommentController.java @@ -1,11 +1,11 @@ -package practice.hyunjincrudapi.comment.controller.dto; +package practice.hyunjincrudapi.comment.controller; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; -import practice.hyunjincrudapi.comment.controller.dto.request.CreateCommentRequest; -import practice.hyunjincrudapi.comment.controller.dto.request.UpdateCommentRequest; -import practice.hyunjincrudapi.comment.controller.dto.response.CommentResponse; +import practice.hyunjincrudapi.comment.dto.request.CreateCommentRequest; +import practice.hyunjincrudapi.comment.dto.request.UpdateCommentRequest; +import practice.hyunjincrudapi.comment.dto.response.CommentResponse; import practice.hyunjincrudapi.comment.service.CommentService; import java.util.List; diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/request/CreateCommentRequest.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/dto/request/CreateCommentRequest.java similarity index 89% rename from week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/request/CreateCommentRequest.java rename to week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/dto/request/CreateCommentRequest.java index adaeb80..99aa5e3 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/request/CreateCommentRequest.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/dto/request/CreateCommentRequest.java @@ -1,4 +1,4 @@ -package practice.hyunjincrudapi.comment.controller.dto.request; +package practice.hyunjincrudapi.comment.dto.request; import lombok.Getter; import practice.hyunjincrudapi.comment.entity.Comment; diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/request/UpdateCommentRequest.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/dto/request/UpdateCommentRequest.java similarity index 77% rename from week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/request/UpdateCommentRequest.java rename to week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/dto/request/UpdateCommentRequest.java index 769b29e..090c228 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/request/UpdateCommentRequest.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/dto/request/UpdateCommentRequest.java @@ -1,4 +1,4 @@ -package practice.hyunjincrudapi.comment.controller.dto.request; +package practice.hyunjincrudapi.comment.dto.request; import jakarta.validation.constraints.NotBlank; import lombok.Getter; diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/response/CommentResponse.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/dto/response/CommentResponse.java similarity index 87% rename from week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/response/CommentResponse.java rename to week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/dto/response/CommentResponse.java index 05d5fdd..a07e66c 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/dto/response/CommentResponse.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/dto/response/CommentResponse.java @@ -1,4 +1,4 @@ -package practice.hyunjincrudapi.comment.controller.dto.response; +package practice.hyunjincrudapi.comment.dto.response; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/service/CommentService.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/service/CommentService.java index b4e5bef..367ca06 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/service/CommentService.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/service/CommentService.java @@ -3,9 +3,9 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import practice.hyunjincrudapi.comment.controller.dto.request.CreateCommentRequest; -import practice.hyunjincrudapi.comment.controller.dto.request.UpdateCommentRequest; -import practice.hyunjincrudapi.comment.controller.dto.response.CommentResponse; +import practice.hyunjincrudapi.comment.dto.request.CreateCommentRequest; +import practice.hyunjincrudapi.comment.dto.request.UpdateCommentRequest; +import practice.hyunjincrudapi.comment.dto.response.CommentResponse; import practice.hyunjincrudapi.comment.entity.Comment; import practice.hyunjincrudapi.comment.repository.JpaCommentRepository; import practice.hyunjincrudapi.member.entity.Member; diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/MemberController.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/MemberController.java similarity index 75% rename from week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/MemberController.java rename to week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/MemberController.java index a8f72f3..3c09150 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/MemberController.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/MemberController.java @@ -1,10 +1,10 @@ -package practice.hyunjincrudapi.member.controller.dto; +package practice.hyunjincrudapi.member.controller; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; -import practice.hyunjincrudapi.member.controller.dto.request.CreateMemberRequest; -import practice.hyunjincrudapi.member.controller.dto.request.UpdateMemberRequest; -import practice.hyunjincrudapi.member.controller.dto.response.MemberResponse; +import practice.hyunjincrudapi.member.dto.request.CreateMemberRequest; +import practice.hyunjincrudapi.member.dto.request.UpdateMemberRequest; +import practice.hyunjincrudapi.member.dto.response.MemberResponse; import practice.hyunjincrudapi.member.service.MemberService; @RestController diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/request/CreateMemberRequest.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/dto/request/CreateMemberRequest.java similarity index 88% rename from week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/request/CreateMemberRequest.java rename to week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/dto/request/CreateMemberRequest.java index 54fa6e1..26e8052 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/request/CreateMemberRequest.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/dto/request/CreateMemberRequest.java @@ -1,4 +1,4 @@ -package practice.hyunjincrudapi.member.controller.dto.request; +package practice.hyunjincrudapi.member.dto.request; import lombok.Getter; import practice.hyunjincrudapi.member.entity.Member; diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/request/UpdateMemberRequest.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/dto/request/UpdateMemberRequest.java similarity index 65% rename from week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/request/UpdateMemberRequest.java rename to week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/dto/request/UpdateMemberRequest.java index c7cf87b..fcf8d9c 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/request/UpdateMemberRequest.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/dto/request/UpdateMemberRequest.java @@ -1,4 +1,4 @@ -package practice.hyunjincrudapi.member.controller.dto.request; +package practice.hyunjincrudapi.member.dto.request; import lombok.Getter; diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/response/MemberResponse.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/dto/response/MemberResponse.java similarity index 85% rename from week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/response/MemberResponse.java rename to week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/dto/response/MemberResponse.java index a013d3e..45ba47f 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/dto/response/MemberResponse.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/dto/response/MemberResponse.java @@ -1,4 +1,4 @@ -package practice.hyunjincrudapi.member.controller.dto.response; +package practice.hyunjincrudapi.member.dto.response; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/service/MemberService.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/service/MemberService.java index cf5e8aa..c5887d4 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/service/MemberService.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/service/MemberService.java @@ -3,9 +3,9 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import practice.hyunjincrudapi.member.controller.dto.request.CreateMemberRequest; -import practice.hyunjincrudapi.member.controller.dto.request.UpdateMemberRequest; -import practice.hyunjincrudapi.member.controller.dto.response.MemberResponse; +import practice.hyunjincrudapi.member.dto.request.CreateMemberRequest; +import practice.hyunjincrudapi.member.dto.request.UpdateMemberRequest; +import practice.hyunjincrudapi.member.dto.response.MemberResponse; import practice.hyunjincrudapi.member.entity.Member; import practice.hyunjincrudapi.member.repository.JpaMemberRepository; diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/PostController.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/PostController.java similarity index 79% rename from week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/PostController.java rename to week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/PostController.java index 0026125..fbc2a20 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/PostController.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/PostController.java @@ -1,11 +1,11 @@ -package practice.hyunjincrudapi.post.controller.dto; +package practice.hyunjincrudapi.post.controller; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; -import practice.hyunjincrudapi.post.controller.dto.request.CreatePostRequest; -import practice.hyunjincrudapi.post.controller.dto.request.UpdatePostRequest; -import practice.hyunjincrudapi.post.controller.dto.response.PostResponse; +import practice.hyunjincrudapi.post.dto.request.CreatePostRequest; +import practice.hyunjincrudapi.post.dto.request.UpdatePostRequest; +import practice.hyunjincrudapi.post.dto.response.PostResponse; import practice.hyunjincrudapi.post.service.PostService; import java.util.List; diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/request/CreatePostRequest.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/dto/request/CreatePostRequest.java similarity index 88% rename from week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/request/CreatePostRequest.java rename to week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/dto/request/CreatePostRequest.java index 3ad19d6..90927b5 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/request/CreatePostRequest.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/dto/request/CreatePostRequest.java @@ -1,4 +1,4 @@ -package practice.hyunjincrudapi.post.controller.dto.request; +package practice.hyunjincrudapi.post.dto.request; import lombok.Getter; import practice.hyunjincrudapi.member.entity.Member; diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/request/UpdatePostRequest.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/dto/request/UpdatePostRequest.java similarity index 82% rename from week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/request/UpdatePostRequest.java rename to week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/dto/request/UpdatePostRequest.java index 2ffb931..b25d8b1 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/request/UpdatePostRequest.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/dto/request/UpdatePostRequest.java @@ -1,4 +1,4 @@ -package practice.hyunjincrudapi.post.controller.dto.request; +package practice.hyunjincrudapi.post.dto.request; import jakarta.validation.constraints.NotBlank; import lombok.Getter; diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/response/PostResponse.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/dto/response/PostResponse.java similarity index 87% rename from week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/response/PostResponse.java rename to week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/dto/response/PostResponse.java index 23631de..17d6486 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/controller/dto/response/PostResponse.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/dto/response/PostResponse.java @@ -1,4 +1,4 @@ -package practice.hyunjincrudapi.post.controller.dto.response; +package practice.hyunjincrudapi.post.dto.response; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/service/PostService.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/service/PostService.java index 331e52e..39974af 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/service/PostService.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/service/PostService.java @@ -5,9 +5,9 @@ import org.springframework.transaction.annotation.Transactional; import practice.hyunjincrudapi.member.entity.Member; import practice.hyunjincrudapi.member.repository.JpaMemberRepository; -import practice.hyunjincrudapi.post.controller.dto.request.CreatePostRequest; -import practice.hyunjincrudapi.post.controller.dto.request.UpdatePostRequest; -import practice.hyunjincrudapi.post.controller.dto.response.PostResponse; +import practice.hyunjincrudapi.post.dto.request.CreatePostRequest; +import practice.hyunjincrudapi.post.dto.request.UpdatePostRequest; +import practice.hyunjincrudapi.post.dto.response.PostResponse; import practice.hyunjincrudapi.post.entity.Post; import practice.hyunjincrudapi.post.repository.JpaPostRepository; From 174a9e19212f0f8857c2b92bd24a3aaab05c8a1b Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Tue, 20 May 2025 20:47:51 +0900 Subject: [PATCH 27/33] =?UTF-8?q?refactor:=20lazy=20loading=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../comment/controller/CommentController.java | 4 ++-- .../hyunjincrudapi/comment/entity/Comment.java | 4 ++-- .../comment/repository/JpaCommentRepository.java | 10 +++++++++- .../hyunjincrudapi/comment/service/CommentService.java | 2 +- .../java/practice/hyunjincrudapi/post/entity/Post.java | 2 +- 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/CommentController.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/CommentController.java index 96e7eed..5b39a52 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/CommentController.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/controller/CommentController.java @@ -21,8 +21,8 @@ public void createComment(@RequestBody CreateCommentRequest request) { } @GetMapping("/posts/{postId}/comments") - public List getCommentsByPost(@PathVariable Long postId) { - return commentService.getCommentsByPost(postId); + public List getCommentsByPostId(@PathVariable Long postId) { + return commentService.getCommentsByPostId(postId); } @PatchMapping("/comments/{id}") diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/entity/Comment.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/entity/Comment.java index e33cc56..328b992 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/entity/Comment.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/entity/Comment.java @@ -18,11 +18,11 @@ public class Comment { private Long id; private String content; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "member_id", nullable = false) private Member member; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name="post_id", nullable = false) private Post post; diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/repository/JpaCommentRepository.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/repository/JpaCommentRepository.java index 340117d..4c5ce3b 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/repository/JpaCommentRepository.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/repository/JpaCommentRepository.java @@ -1,11 +1,19 @@ package practice.hyunjincrudapi.comment.repository; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import practice.hyunjincrudapi.comment.entity.Comment; import java.util.List; public interface JpaCommentRepository extends JpaRepository { - List findByPostId(Long postId); + @Query(""" + SELECT c FROM Comment c + JOIN FETCH c.member + JOIN FETCH c.post + WHERE c.post.id = :postId + """) + List findByPostId(@Param("postId")Long postId); } diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/service/CommentService.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/service/CommentService.java index 367ca06..1161dd9 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/service/CommentService.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/comment/service/CommentService.java @@ -36,7 +36,7 @@ public void createComment(CreateCommentRequest request) { commentRepository.save(comment); } - public List getCommentsByPost(Long postId){ + public List getCommentsByPostId(Long postId){ return commentRepository.findByPostId(postId).stream() .map(CommentResponse::from) .toList(); diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/entity/Post.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/entity/Post.java index 3983bc8..c7cc453 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/entity/Post.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/post/entity/Post.java @@ -22,7 +22,7 @@ public class Post { private String title; private String content; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "member_id", nullable = false) private Member member; From ef0af09e4ebf531513baa780ef5e5c0ec4ea5fec Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Thu, 22 May 2025 19:34:54 +0900 Subject: [PATCH 28/33] =?UTF-8?q?docs:=20=EB=8F=84=EC=BB=A4=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- week8/hyunjin-crud-api/Dockerfile | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 week8/hyunjin-crud-api/Dockerfile diff --git a/week8/hyunjin-crud-api/Dockerfile b/week8/hyunjin-crud-api/Dockerfile new file mode 100644 index 0000000..cf889e0 --- /dev/null +++ b/week8/hyunjin-crud-api/Dockerfile @@ -0,0 +1,5 @@ +FROM eclipse-temurin:17-jdk-jammy +VOLUME /tmp +ARG JAR_FILE=build/libs/*jar +COPY ${JAR_FILE} app.jar +ENTRYPOINT ["java", "-jar", "/app.jar"] From 09507d1a76111b8ba9510015148353debda53777 Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Thu, 22 May 2025 19:35:19 +0900 Subject: [PATCH 29/33] =?UTF-8?q?feat:=20workflows=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yml | 42 ++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/workflows/deploy.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..afdd544 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,42 @@ +name: Deploy to EC2 with Docker + +on: + push: + branches: + - main + +jobs: + deploy: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Build and push Docker image + run: | + docker buildx build \ + --platform linux/amd64 \ + -t ${{ secrets.DOCKER_USERNAME }}/hyunjin-crud-api:latest \ + --push ./week8/hyunjin-crud-api + + - name: Deploy to EC2 + uses: appleboy/ssh-action@v1.0.0 + with: + host: ${{ secrets.EC2_HOST }} + username: ${{ secrets.EC2_USER }} + key: ${{ secrets.EC2_SSH_KEY }} + script: | + docker pull ${{ secrets.DOCKER_USERNAME }}/hyunjin-crud-api:latest + docker stop spring-app || true + docker rm spring-app || true + docker run -d --name spring-app -p 8080:8080 ${{ secrets.DOCKER_USERNAME }}/hyunjin-crud-api:latest From a67ca525b0915a565b6b6715df9cc912fd7011d8 Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Thu, 22 May 2025 19:37:31 +0900 Subject: [PATCH 30/33] =?UTF-8?q?refactor:=20deploy=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index afdd544..abab5f9 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -13,6 +13,18 @@ jobs: - name: Checkout code uses: actions/checkout@v3 + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: '17' + + - name: Grant execute permission for gradlew + run: chmod +x ./week8/hyunjin-crud-api/gradlew + + - name: Build with Gradle + run: ./week8/hyunjin-crud-api/gradlew build --no-daemon + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 From 977cf9921ce9511c824dd13b2435cd7c48bb0aad Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Thu, 22 May 2025 19:52:30 +0900 Subject: [PATCH 31/33] =?UTF-8?q?refactor:=20=EB=B2=84=EC=A0=84=EC=9D=B4?= =?UTF-8?q?=20=EC=97=86=EB=8A=94=20=ED=8C=8C=EC=9D=BC=20push?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- week8/hyunjin-crud-api/build.gradle | 43 +++ .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43705 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + week8/hyunjin-crud-api/gradlew | 251 ++++++++++++++++++ week8/hyunjin-crud-api/gradlew.bat | 94 +++++++ week8/hyunjin-crud-api/settings.gradle | 1 + .../HyunjinCrudApiApplication.java | 13 + .../src/main/resources/application.yml | 36 +++ 8 files changed, 445 insertions(+) create mode 100644 week8/hyunjin-crud-api/build.gradle create mode 100644 week8/hyunjin-crud-api/gradle/wrapper/gradle-wrapper.jar create mode 100644 week8/hyunjin-crud-api/gradle/wrapper/gradle-wrapper.properties create mode 100755 week8/hyunjin-crud-api/gradlew create mode 100644 week8/hyunjin-crud-api/gradlew.bat create mode 100644 week8/hyunjin-crud-api/settings.gradle create mode 100644 week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/HyunjinCrudApiApplication.java create mode 100644 week8/hyunjin-crud-api/src/main/resources/application.yml diff --git a/week8/hyunjin-crud-api/build.gradle b/week8/hyunjin-crud-api/build.gradle new file mode 100644 index 0000000..8ff9b70 --- /dev/null +++ b/week8/hyunjin-crud-api/build.gradle @@ -0,0 +1,43 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '3.4.5' + id 'io.spring.dependency-management' version '1.1.7' +} + +group = 'practice' +version = '0.0.1-SNAPSHOT' + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-jdbc' + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'jakarta.validation:jakarta.validation-api:3.0.2' + compileOnly 'org.projectlombok:lombok' + developmentOnly 'org.springframework.boot:spring-boot-devtools' + developmentOnly 'org.springframework.boot:spring-boot-docker-compose' + runtimeOnly 'com.mysql:mysql-connector-j' + annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' +} + +tasks.named('test') { + useJUnitPlatform() +} diff --git a/week8/hyunjin-crud-api/gradle/wrapper/gradle-wrapper.jar b/week8/hyunjin-crud-api/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..9bbc975c742b298b441bfb90dbc124400a3751b9 GIT binary patch literal 43705 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 DHx0+F literal 0 HcmV?d00001 diff --git a/week8/hyunjin-crud-api/gradle/wrapper/gradle-wrapper.properties b/week8/hyunjin-crud-api/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..37f853b --- /dev/null +++ b/week8/hyunjin-crud-api/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/week8/hyunjin-crud-api/gradlew b/week8/hyunjin-crud-api/gradlew new file mode 100755 index 0000000..faf9300 --- /dev/null +++ b/week8/hyunjin-crud-api/gradlew @@ -0,0 +1,251 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/week8/hyunjin-crud-api/gradlew.bat b/week8/hyunjin-crud-api/gradlew.bat new file mode 100644 index 0000000..9d21a21 --- /dev/null +++ b/week8/hyunjin-crud-api/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/week8/hyunjin-crud-api/settings.gradle b/week8/hyunjin-crud-api/settings.gradle new file mode 100644 index 0000000..496e64c --- /dev/null +++ b/week8/hyunjin-crud-api/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'hyunjin-crud-api' diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/HyunjinCrudApiApplication.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/HyunjinCrudApiApplication.java new file mode 100644 index 0000000..5c3488c --- /dev/null +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/HyunjinCrudApiApplication.java @@ -0,0 +1,13 @@ +package practice.hyunjincrudapi; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class HyunjinCrudApiApplication { + + public static void main(String[] args) { + SpringApplication.run(HyunjinCrudApiApplication.class, args); + } + +} diff --git a/week8/hyunjin-crud-api/src/main/resources/application.yml b/week8/hyunjin-crud-api/src/main/resources/application.yml new file mode 100644 index 0000000..1ca5b9c --- /dev/null +++ b/week8/hyunjin-crud-api/src/main/resources/application.yml @@ -0,0 +1,36 @@ +spring: + application: + name: name-crud-api2 + jpa: + open-in-view: true + hibernate: + ddl-auto: create + show-sql: true + properties: + hibernate: + format_sql: true + highlight_sql: true + datasource: + url: jdbc:mysql://cow-database.chmg4e88k39w.ap-northeast-2.rds.amazonaws.com/cow?useSSL=false&serverTimezone=Asia/Seoul&characterEncoding=UTF-8 + driver-class-name: com.mysql.cj.jdbc.Driver + username: admin + password: tls2530. + + docker: + compose: + file: compose.yaml + enabled: true + lifecycle-management: none + start: + command: up + stop: + command: down + timeout: 1m + +logging: + level: + org: + hibernate: + type: + descriptor: + sql: trace From fb0ee86f2c390fd7c78d15f22a30a2e1caca5123 Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Thu, 22 May 2025 20:01:36 +0900 Subject: [PATCH 32/33] =?UTF-8?q?refactor:=20deploy=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index abab5f9..ba9c991 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -19,11 +19,13 @@ jobs: distribution: 'temurin' java-version: '17' - - name: Grant execute permission for gradlew - run: chmod +x ./week8/hyunjin-crud-api/gradlew + - name: Grant execute permission to gradlew + run: chmod +x gradlew + working-directory: week8/hyunjin-crud-api - name: Build with Gradle - run: ./week8/hyunjin-crud-api/gradlew build --no-daemon + run: ./gradlew build --no-daemon + working-directory: week8/hyunjin-crud-api - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -39,7 +41,8 @@ jobs: docker buildx build \ --platform linux/amd64 \ -t ${{ secrets.DOCKER_USERNAME }}/hyunjin-crud-api:latest \ - --push ./week8/hyunjin-crud-api + --push . + working-directory: week8/hyunjin-crud-api - name: Deploy to EC2 uses: appleboy/ssh-action@v1.0.0 From d8e4d06531d04108e0b82bfad0291a4bc95a0906 Mon Sep 17 00:00:00 2001 From: xxxjinn Date: Thu, 22 May 2025 20:06:30 +0900 Subject: [PATCH 33/33] =?UTF-8?q?test:=20ci/cd=20=EC=9E=90=EB=8F=99?= =?UTF-8?q?=ED=99=94=20=ED=85=8C=EC=8A=A4=ED=8A=B8=EB=A5=BC=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hyunjincrudapi/member/controller/MemberController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/MemberController.java b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/MemberController.java index 3c09150..c941662 100644 --- a/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/MemberController.java +++ b/week8/hyunjin-crud-api/src/main/java/practice/hyunjincrudapi/member/controller/MemberController.java @@ -9,7 +9,7 @@ @RestController @RequiredArgsConstructor -@RequestMapping("/members") +@RequestMapping("/members1") public class MemberController { private final MemberService memberService;