diff --git a/week3/README.md b/week3/README.md index 945c9b4..9c558e3 100644 --- a/week3/README.md +++ b/week3/README.md @@ -1 +1 @@ -. \ No newline at end of file +. diff --git "a/week3/[3\354\243\274\354\260\250]\354\234\240\355\232\250\354\204\235.md" "b/week3/[3\354\243\274\354\260\250]\354\234\240\355\232\250\354\204\235.md" new file mode 100644 index 0000000..6475ed6 --- /dev/null +++ "b/week3/[3\354\243\274\354\260\250]\354\234\240\355\232\250\354\204\235.md" @@ -0,0 +1,47 @@ +* public static final int => #define과 거의 유사! +* List 활용법 +1) 정의: import java.util.List; + List< type> name; 이런 식으로 정의 + +* 겹치지 않고 정수 리스트 생성법 +### 1) 가장 단순한 방법: +- 랜덤으로 숫자 생성 -> 기존의 리스트와 비교해서 없으면 ok 아니면 다시! count 세서 원하는 만큼 while loop +### 2) set이용! +- set으로 랜덤 넘버 생성 -> list로 변환 +- import java.util.Set; + +Set uniqueNumbers = new HashSet<>(); +int min = 1; +int max = 45; +count = 6; + +while(uniqueNumebr.size() < count){ +int num = random.nextInt(max - min +1) + min; +uniqueNubers.add(num); +} + +List list = new ArrayList +### 3) 오름차순 정렬방법 +- Collections.sort( list ); + +// 내부 list를 자체로 바꿈. 즉, return하는 것이 아니고 리스트의 값들에 접근해서 순서를 바꾸고 영구적으로 저장함. + +### 4) 생성자 주입(DI)과 AppConfig +- 클라이언트 class에서 A class가 필요할 때, 클라이언트 class 내부에서 바로 A class를 참조하는 것은 DIP원칙에 어긋난다. +- A class보다 상위 요소를 참조하는 것이 필요함. 따라서 A class의 상위요소(인터페이스)를 클라이언트 class가 가지고 있는 것이 바람직하다. +- 이때 생성자를 이용하여 참조하는 것 보단, 의존관계와 의존성 주입을 AppConfig class에서 따로 해주는 것이 적절함. +- 이 과정을 spring이 자동으로 해줄 수도 있음 + +### 5) class 간의 역할 분배 +- class를 각 기능별로 생성하는 것은 책임을 너무 분배하는 것.. +- class 내부에서도 충분히 메소드를 이용하여 책임을 분리할 수 있다! +- validate과 같은 메소드는 따로 validator를 이용한 처리보다 각 객체가 생성될 때 검사하는 것이 좋다. +- magic number 또한 magic number들을 묶어 놓은 class를 생성하기 보단 각 class가 가지고 있는 것이 불필요한 수정을 막을 수 있다. + +### 6) 궁금한 점 +- magic number를 각 class가 가지고 있을 때, 외부의 class 내부에서 해당 magic number가 필요한 경우 그대로 참조하는 것이 괜찮을까? + // 외부의 class에서도 magic number 따로 선언해야 할까? - 원래 magic number를 가지고 있는 class의 정보를 외부에서 따로 정의하고 있는 것이 좋아보이진 않는데.. + +### 7) 느낀점 +- MVC 패턴에 대한 공부가 더 필요하다고 느꼈다. 각 블로그와 블로그의 예제들을 몇번 작성해 보았지만 아직도, controller의 역할을 이해하기 어려웠다. + controller와 service와의 차이점을 이해하기 어려웠다. +- AppConfig도 아직 완벽하게 이해되지 않아서 예제 코드를 몇번 더 써봐야 할 것 같다. \ No newline at end of file diff --git a/week7/README.md b/week7/README.md index 29f221a..caa9d21 100644 --- a/week7/README.md +++ b/week7/README.md @@ -32,4 +32,5 @@ Mysql: https://dev.mysql.com/downloads/mysql/ ![img_1.png](img_1.png) 4. 의존성(Dependencies): Spring Web, Spring Boot DevTools, Lombok, Spring Data JPA 선택 -5. Create 눌러서 선택 + +5. Create 눌러서 선택 \ No newline at end of file diff --git "a/week7/[7\354\243\274\354\260\250]\354\234\240\355\232\250\354\204\235.md" "b/week7/[7\354\243\274\354\260\250]\354\234\240\355\232\250\354\204\235.md" new file mode 100644 index 0000000..b50f410 --- /dev/null +++ "b/week7/[7\354\243\274\354\260\250]\354\234\240\355\232\250\354\204\235.md" @@ -0,0 +1,45 @@ +# 7주차 과제 + +### 스프링 어노테이션 +- 코드 사이에 특별한 의미, 기능을 수행하도록 하는 기술. +- 함수의 행동을 바꾸지는 않지만, 어노테이션에 대응 하는 processor가 어노테이션에 맞는 적절한 행동을 하게 해주는 마커. + + +- 사용 순서: + 1. 어노테이션 정의 + 2. 클래스에 어노테이션 배치 + 3. 실행 중, reflection 이용 -> 추가 정보 획득 -> 기능 실시 + +- 종류 + - @Configuration: Bean 정의의 소스로 쓰이는 클래스 마크. + - @Bean: 메서드에서 반환된 결과를 빈으로 저장하는 애너테이션. + - @Autowired: 스프링이 의존성 주입을 할 클래스를 명시하는 마크. + - @Override: + + +### 스프링 컨테이너 +- 컨테이너가 생성하고 관리하는 객체 = bean(어노테이션으로 추가 할 수 있음) +- 스프링 컨테이너 = bean의 생명주기를 관리하는(생성/관리/제거) 역할. +- 생성자의 사용을 줄여 -> 객체간의 결합도를 낮출 수 있음. +- 객체들의 의존성을 줄이고 인터페이스만 참조하도록 설계 가능. +- 컨테이너, 애플리케이션 시작할 때 빈 정의를 로드 -> 인스턴스 생성 + +### 스프링의 의존성 주입 +- AppConfig를 사용하여 직접 의존성을 주입하지 않고 Spring Boot에서 자동으로 객체간 의존관계를 설정해주는 것. +- @Component 어노테이션이 붙은 클래스들을 스캔한다. +- 스캔한 클래스들을 빈으로 등록한다. + - @Autowired 어노테이션이 붙은 클래스들 타입의 빈을 찾아 의존성을 주입한다. + - ex) + @Component // 빈으로 등록할 객체 + public interface MyRepository{ + + } + + public class MyService { + private final MyRepository myRepository; + + @Autowired // 여기에 의존성 주입을 해줘..! + public MyService(MyRepository myRepository){ + this.myRepository = myRepository; + } + } \ No newline at end of file diff --git "a/week8/\354\234\240\355\232\250\354\204\235/Application.java" "b/week8/\354\234\240\355\232\250\354\204\235/Application.java" new file mode 100644 index 0000000..9086057 --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/Application.java" @@ -0,0 +1,13 @@ +package com.example; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git "a/week8/\354\234\240\355\232\250\354\204\235/README.md" "b/week8/\354\234\240\355\232\250\354\204\235/README.md" new file mode 100644 index 0000000..bfb2f0b --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/README.md" @@ -0,0 +1,29 @@ +1. POST +- POST 실행 + ![스크린샷](https://github.com/user-attachments/assets/dff28d7a-cdfe-46c5-95a9-b1da38646482) + id가 5인 새로운 user 등록 + + +- POST 결과 +![스크린샷](https://github.com/user-attachments/assets/dd049556-e702-4ea3-8a49-1e948f94d00) + + +2. PUT +- PUT 실행 +![스크린샷](https://github.com/user-attachments/assets/a4a7486d-45fa-4166-b223-947de5cf64be) +id가 1인 user의 name과 email을 수정함. + + +- PUT 결과 +![스크린샷](https://github.com/user-attachments/assets/4fe5d716-aabe-41d6-a12d-7f392f93b719) + + +3. DELETE +- DELETE 실행 +![스크린샷](https://github.com/user-attachments/assets/2ef36d24-14b4-4218-900b-4a9b08a12f6a) +id가 1인 user를 삭제함. + + +- DELETE 결과 +![스크린샷](https://github.com/user-attachments/assets/0e1a5483-0180-4926-b293-f57a440a0755) + diff --git "a/week8/\354\234\240\355\232\250\354\204\235/[8\354\243\274\354\260\250 \354\234\240\355\232\250\354\204\235].md" "b/week8/\354\234\240\355\232\250\354\204\235/[8\354\243\274\354\260\250 \354\234\240\355\232\250\354\204\235].md" new file mode 100644 index 0000000..273fe8f --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/[8\354\243\274\354\260\250 \354\234\240\355\232\250\354\204\235].md" @@ -0,0 +1,46 @@ +service - controller 분리! +// controller는 요청 + 검증 -> 절대 DB를 알면 안돼!! +// service는 실제 업무 처리 w/ DB(repository) +// repository는 실제 DB와 소통하며 데이터 관리 + +USER INPUT -> controller -> service -> repository -> DB + +controller: JASON -> DTO와 매칭 + service에게 업무 맡겨 +service: repository를 이용하여 데이터 저장/불러오기 + + +[어노테이션] +@RequiredArgsConstructor +- 생성자를 만들지 않아도 됨. + +public UserService(UserRepository userRepository) { + this.userRepository = userRepository; +} + +이 코드 자동으로 생성 + +// service는 controller 너무 많은 것을 알지 않도록 중간 다리 역할을 해줌 + + + +//변경이 존재하지 않음! +// 차피 DTO로 Json에서 넘겨 받을 거고 넘겨줄 때도 DTO로 할거니까! + +[어노테이션] +@RestController +JSON요청/응답 자동처리 +-> postman에서 body로 json보내면 자동으로 객체로 변환 + +@RequestMapping("/users") +이 컨트롤러의 기본 주소를 /users로 설정 + +JPA는 어떻게 변하는가? + +DTO -> Entity로 변환 at service -> Repository = entity로 저장 + +CRUD + +Create - INSERT +Read - SELECT +Update - UPDATE +Delete - DELETE diff --git "a/week8/\354\234\240\355\232\250\354\204\235/[9\354\243\274\354\260\250 \354\234\240\355\232\250\354\204\235].md" "b/week8/\354\234\240\355\232\250\354\204\235/[9\354\243\274\354\260\250 \354\234\240\355\232\250\354\204\235].md" new file mode 100644 index 0000000..1e06326 --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/[9\354\243\274\354\260\250 \354\234\240\355\232\250\354\204\235].md" @@ -0,0 +1,69 @@ +### 연관 관계 다중성 +1. 나의 개수 to 너의 개수 +2. 종류 + - Many to One (= 학생 입장에서 학교는 다대일) + - One to many (= 학교 입장에서 각 학생은 일대다) + - One to One + - Many to Many +3. 방향성 + - 종류 + + 단방향: 한쪽의 엔티티만 상대 엔티티 참조 + + 양방향: 양쪽이 서로 참조 + + +4. 어노테이션 + - @ManyToOne + + 다대일 관계 매핑 + + fetch: 연관 관계의 데이터를 어떻게 가져올 지 + * Eager Loading = 즉시 로딩; 엔티티 조회 시, 연관된 모든 데이터 로딩 // default임. + * Lazy Loading = 지연 로딩; 객체의 초기화를 최대한 지연, 주로 권장됨. + * ex) @ManyToOne(fetch = FetchType.LAZY) + + +- @OneToMany + + 일대다 관계 매핑 + + mappedBy = 연관관계의 주인 설정 + * ex) mappedBy = "post" + + cascade = 엔티티 작업 수행시, 연관 엔티티도 동일한 작업 수행 + * Type: PERSIST, MERGE, DETACH, PREFRESH, REMOVE, ALL + * ex) cascade = CascadeType.ALL +------------------- + +## 코드 설계 + +### 기능 +1. 회원 + - 등록 = POSTMAN으로 함. + +2. 게시판 + - 게시글 / 댓글 조회 / 수정 / 삭제 + - 수정 삭제 = 작성자 + 관리자만 + +3. Entity 구성 + - User + - UserService + - UserRepository + - UserController + - Post(게시글) + - Comment + + #### User + + 기존 user를 그대로 + + role을 추가해서 관리자 여부 설정 + + #### Post + + id, title, content + + update : 게시글 수정 + + comment를 list로 가지고 있음. + + addComment : 댓글 추가 + + deleteComment : 댓글 삭제 + + +4. entity 관계 설정 + - 회원은 게시판과 일대다 관계 + - 회원은 댓글과 일대다 관계 + - 게시판은 댓글들과 일대다 관계 + +-------------------- +###새롭게 알게 된 것들 +- @GeneratedValue => 값이 입력되지 않았을 때 자동으로 값을 넣어줌. \ No newline at end of file diff --git "a/week8/\354\234\240\355\232\250\354\204\235/controller/PostController.java" "b/week8/\354\234\240\355\232\250\354\204\235/controller/PostController.java" new file mode 100644 index 0000000..c626542 --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/controller/PostController.java" @@ -0,0 +1,42 @@ +package com.example.controller; + +import com.example.entity.Post; +import com.example.service.PostService; +import com.example.dto.PostDto; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/posts") + +public class PostController { + private final PostService postService; + + @PostMapping + public Long createPost(@RequestBody PostDto postDto) { + return postService.createPost(postDto); + } + + @GetMapping + public List getPosts(){ + return postService.findAllPosts().stream().map(PostDto::fromEntity).toList(); + } + + @GetMapping("/{id}") + public PostDto getPostById(@PathVariable Long postId){ + Post post = postService.getPostById(postId); + return PostDto.fromEntity(post); + } + + @DeleteMapping("/{id}") + public void deletePost(@PathVariable Long postId, @RequestParam Long authorId){ + postService.deletePost(postId, authorId); + } + +} diff --git "a/week8/\354\234\240\355\232\250\354\204\235/controller/UserController.java" "b/week8/\354\234\240\355\232\250\354\204\235/controller/UserController.java" new file mode 100644 index 0000000..d5d52e0 --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/controller/UserController.java" @@ -0,0 +1,44 @@ +package com.example.controller; + +import com.example.dto.UserDto; +import com.example.repository.UserRepository; +import com.example.service.UserService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/users") // 필수는 아니지만 있으면 편함. // 동사가 들어가지 않는다. +public class UserController { + private final UserService userService; + private final UserRepository userRepository; + + //jason으로 요청이 옴. + @PostMapping // 유저 생성하는 것. 자동으로 json -> DTO 생성 + public void createUser(@RequestBody UserDto userDto) { + userService.addUser(userDto); + } + + @GetMapping + public List getUsers(){ // 전체 유저 넘겨주는 것. + return userService.getAllUsers(); + } + + @GetMapping("/{id}") + public UserDto getUser(@PathVariable Long id) { + return userService.getUser(id); + } + + @PutMapping("/{id}") + public void updateUser(@PathVariable Long id, @RequestBody UserDto dto){ + userService.updateUser(id, dto); + } + + @DeleteMapping("/{id}") + public void deleteUser(@PathVariable Long id) { + userService.deleteUser(id); + } + +} diff --git "a/week8/\354\234\240\355\232\250\354\204\235/dto/CommentDto.java" "b/week8/\354\234\240\355\232\250\354\204\235/dto/CommentDto.java" new file mode 100644 index 0000000..27c51c4 --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/dto/CommentDto.java" @@ -0,0 +1,29 @@ +package com.example.dto; + +import com.example.entity.Comment; +import com.example.entity.User; +import com.example.entity.Post; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CommentDto { + + private Long id; + private Long postId; + private Long authorId; + private String content; + + //DTO -> entity + // entity -> DTO + + public Comment toEntity(User author, Post post){ + return Comment.builder().content(this.content).author(author).post(post).build(); + } +} diff --git "a/week8/\354\234\240\355\232\250\354\204\235/dto/PostDto.java" "b/week8/\354\234\240\355\232\250\354\204\235/dto/PostDto.java" new file mode 100644 index 0000000..00bf24c --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/dto/PostDto.java" @@ -0,0 +1,29 @@ +package com.example.dto; + +import lombok.*; + +import com.example.entity.Post; +import com.example.entity.User; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PostDto { + //기능 = DTO -> entity + // = entity -> DTO + + private Long id; // post 자체의 id + private Long authorId; // author의 id가 더 필요함. 삭제/수정 권한 때문에 + private String title; + private String content; + + public Post toEntity(User author) { + return Post.builder().title(title).content(content).author(author).build(); + } + + public static PostDto fromEntity(Post post) { + return PostDto.builder().id(post.getId()) + .authorId(post.getAuthor().getId()).title(post.getTitle()).content(post.getContent()).build(); } +} + diff --git "a/week8/\354\234\240\355\232\250\354\204\235/dto/UserDto.java" "b/week8/\354\234\240\355\232\250\354\204\235/dto/UserDto.java" new file mode 100644 index 0000000..0b6b7fe --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/dto/UserDto.java" @@ -0,0 +1,35 @@ +package com.example.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import com.example.entity.User; +import com.exaple.entity.UserRole + +@Getter +@Builder +@AllArgsConstructor // 생성자를 자동으로 만들어주는 것. +@NoArgsConstructor + +public class UserDto { // data를 front-end로 전달 + + private Long id; + private String name; + private String email; + private UserRole role; + + + public User toEntity(){ + + User user = User.builder().id(id).name(name).email(email).userRole(role).build(); + return user; + } + + public static UserDto from(User user){ + return UserDto.builder().id(user.getId()).name(user.getName()) + .email(user.getEmail()).role(user.getUserRole()).build(); + } + + +} diff --git "a/week8/\354\234\240\355\232\250\354\204\235/entity/Comment.java" "b/week8/\354\234\240\355\232\250\354\204\235/entity/Comment.java" new file mode 100644 index 0000000..5ae4351 --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/entity/Comment.java" @@ -0,0 +1,33 @@ +package com.example.entity; + +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@Builder + +public class Comment { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String content; + + @ManyToOne(fetch = FetchType.LAZY) // 댓글 입장에선 author이 다대일 관계임. + private User author; + + @ManyToOne(fetch = FetchType.LAZY) + private Post post; + + public void update(String content){ + this.content = content; + } + + public void setPost(Post post){ + this.post = post; + } + +} diff --git "a/week8/\354\234\240\355\232\250\354\204\235/entity/Post.java" "b/week8/\354\234\240\355\232\250\354\204\235/entity/Post.java" new file mode 100644 index 0000000..d7a3e0d --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/entity/Post.java" @@ -0,0 +1,47 @@ +package com.example.entity; + +import jakarta.persistence.*; +import lombok.*; +import com.example.entity.Comment; + +// import java.util.List; +import java.util.ArrayList; + +import java.util.*; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@Builder + +public class Post { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) // id가 설정이 안될 때 자동으로 값을 넣어줌! + private long id; + + private String title; + private String content; + + @ManyToOne(fetch = FetchType.LAZY) // 다중성 관계 설정 게시판 입장에선 user와 다대일 + private User author; // user를 작성자로 받아온다. + + @OneToMany(fetch = FetchType.LAZY) // 게시판 입장에선 comment와 일대다 + private List comments = new ArrayList<>(); + + public void update(String title, String content){ + this.title = title; + this.content = content; + } + + public void addComment(Comment comment){ + comments.add(comment); + comment.setPost(this); + } + + public void removeComment(Comment comment){ + comments.remove(comment); + comment.setPost(null); + } +} diff --git "a/week8/\354\234\240\355\232\250\354\204\235/entity/User.java" "b/week8/\354\234\240\355\232\250\354\204\235/entity/User.java" new file mode 100644 index 0000000..794caae --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/entity/User.java" @@ -0,0 +1,33 @@ +package com.example.entity; + +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Table(name = "users") +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) // 생성자 -> protected로 생성 +@AllArgsConstructor +@Builder + +public class User { + + @Id// primary key = id다 설정 + @GeneratedValue(strategy = GenerationType.IDENTITY) // id가 안들어오면 자동으로 넣어줘. + private Long id; + + @Enumerated(EnumType.STRING) + private UserRole userRole; + private String name; + private String email; + + public void update(String name, String email) { + this.name = name; + this.email = email; + } + + public boolean isAdmin() { + return userRole == UserRole.ADMIN; + } + +} diff --git "a/week8/\354\234\240\355\232\250\354\204\235/entity/UserRole.java" "b/week8/\354\234\240\355\232\250\354\204\235/entity/UserRole.java" new file mode 100644 index 0000000..0ce43f0 --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/entity/UserRole.java" @@ -0,0 +1,5 @@ +package com.example.entity; + +public enum UserRole { + USER, ADMIN +} diff --git "a/week8/\354\234\240\355\232\250\354\204\235/example/Application.java" "b/week8/\354\234\240\355\232\250\354\204\235/example/Application.java" new file mode 100644 index 0000000..9086057 --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/example/Application.java" @@ -0,0 +1,13 @@ +package com.example; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git "a/week8/\354\234\240\355\232\250\354\204\235/example/[8\354\243\274\354\260\250 \354\234\240\355\232\250\354\204\235].md" "b/week8/\354\234\240\355\232\250\354\204\235/example/[8\354\243\274\354\260\250 \354\234\240\355\232\250\354\204\235].md" new file mode 100644 index 0000000..273fe8f --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/example/[8\354\243\274\354\260\250 \354\234\240\355\232\250\354\204\235].md" @@ -0,0 +1,46 @@ +service - controller 분리! +// controller는 요청 + 검증 -> 절대 DB를 알면 안돼!! +// service는 실제 업무 처리 w/ DB(repository) +// repository는 실제 DB와 소통하며 데이터 관리 + +USER INPUT -> controller -> service -> repository -> DB + +controller: JASON -> DTO와 매칭 + service에게 업무 맡겨 +service: repository를 이용하여 데이터 저장/불러오기 + + +[어노테이션] +@RequiredArgsConstructor +- 생성자를 만들지 않아도 됨. + +public UserService(UserRepository userRepository) { + this.userRepository = userRepository; +} + +이 코드 자동으로 생성 + +// service는 controller 너무 많은 것을 알지 않도록 중간 다리 역할을 해줌 + + + +//변경이 존재하지 않음! +// 차피 DTO로 Json에서 넘겨 받을 거고 넘겨줄 때도 DTO로 할거니까! + +[어노테이션] +@RestController +JSON요청/응답 자동처리 +-> postman에서 body로 json보내면 자동으로 객체로 변환 + +@RequestMapping("/users") +이 컨트롤러의 기본 주소를 /users로 설정 + +JPA는 어떻게 변하는가? + +DTO -> Entity로 변환 at service -> Repository = entity로 저장 + +CRUD + +Create - INSERT +Read - SELECT +Update - UPDATE +Delete - DELETE diff --git "a/week8/\354\234\240\355\232\250\354\204\235/example/[9\354\243\274\354\260\250 \354\234\240\355\232\250\354\204\235].md" "b/week8/\354\234\240\355\232\250\354\204\235/example/[9\354\243\274\354\260\250 \354\234\240\355\232\250\354\204\235].md" new file mode 100644 index 0000000..1e06326 --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/example/[9\354\243\274\354\260\250 \354\234\240\355\232\250\354\204\235].md" @@ -0,0 +1,69 @@ +### 연관 관계 다중성 +1. 나의 개수 to 너의 개수 +2. 종류 + - Many to One (= 학생 입장에서 학교는 다대일) + - One to many (= 학교 입장에서 각 학생은 일대다) + - One to One + - Many to Many +3. 방향성 + - 종류 + + 단방향: 한쪽의 엔티티만 상대 엔티티 참조 + + 양방향: 양쪽이 서로 참조 + + +4. 어노테이션 + - @ManyToOne + + 다대일 관계 매핑 + + fetch: 연관 관계의 데이터를 어떻게 가져올 지 + * Eager Loading = 즉시 로딩; 엔티티 조회 시, 연관된 모든 데이터 로딩 // default임. + * Lazy Loading = 지연 로딩; 객체의 초기화를 최대한 지연, 주로 권장됨. + * ex) @ManyToOne(fetch = FetchType.LAZY) + + +- @OneToMany + + 일대다 관계 매핑 + + mappedBy = 연관관계의 주인 설정 + * ex) mappedBy = "post" + + cascade = 엔티티 작업 수행시, 연관 엔티티도 동일한 작업 수행 + * Type: PERSIST, MERGE, DETACH, PREFRESH, REMOVE, ALL + * ex) cascade = CascadeType.ALL +------------------- + +## 코드 설계 + +### 기능 +1. 회원 + - 등록 = POSTMAN으로 함. + +2. 게시판 + - 게시글 / 댓글 조회 / 수정 / 삭제 + - 수정 삭제 = 작성자 + 관리자만 + +3. Entity 구성 + - User + - UserService + - UserRepository + - UserController + - Post(게시글) + - Comment + + #### User + + 기존 user를 그대로 + + role을 추가해서 관리자 여부 설정 + + #### Post + + id, title, content + + update : 게시글 수정 + + comment를 list로 가지고 있음. + + addComment : 댓글 추가 + + deleteComment : 댓글 삭제 + + +4. entity 관계 설정 + - 회원은 게시판과 일대다 관계 + - 회원은 댓글과 일대다 관계 + - 게시판은 댓글들과 일대다 관계 + +-------------------- +###새롭게 알게 된 것들 +- @GeneratedValue => 값이 입력되지 않았을 때 자동으로 값을 넣어줌. \ No newline at end of file diff --git "a/week8/\354\234\240\355\232\250\354\204\235/example/controller/PostController.java" "b/week8/\354\234\240\355\232\250\354\204\235/example/controller/PostController.java" new file mode 100644 index 0000000..c626542 --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/example/controller/PostController.java" @@ -0,0 +1,42 @@ +package com.example.controller; + +import com.example.entity.Post; +import com.example.service.PostService; +import com.example.dto.PostDto; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/posts") + +public class PostController { + private final PostService postService; + + @PostMapping + public Long createPost(@RequestBody PostDto postDto) { + return postService.createPost(postDto); + } + + @GetMapping + public List getPosts(){ + return postService.findAllPosts().stream().map(PostDto::fromEntity).toList(); + } + + @GetMapping("/{id}") + public PostDto getPostById(@PathVariable Long postId){ + Post post = postService.getPostById(postId); + return PostDto.fromEntity(post); + } + + @DeleteMapping("/{id}") + public void deletePost(@PathVariable Long postId, @RequestParam Long authorId){ + postService.deletePost(postId, authorId); + } + +} diff --git "a/week8/\354\234\240\355\232\250\354\204\235/example/controller/UserController.java" "b/week8/\354\234\240\355\232\250\354\204\235/example/controller/UserController.java" new file mode 100644 index 0000000..d5d52e0 --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/example/controller/UserController.java" @@ -0,0 +1,44 @@ +package com.example.controller; + +import com.example.dto.UserDto; +import com.example.repository.UserRepository; +import com.example.service.UserService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/users") // 필수는 아니지만 있으면 편함. // 동사가 들어가지 않는다. +public class UserController { + private final UserService userService; + private final UserRepository userRepository; + + //jason으로 요청이 옴. + @PostMapping // 유저 생성하는 것. 자동으로 json -> DTO 생성 + public void createUser(@RequestBody UserDto userDto) { + userService.addUser(userDto); + } + + @GetMapping + public List getUsers(){ // 전체 유저 넘겨주는 것. + return userService.getAllUsers(); + } + + @GetMapping("/{id}") + public UserDto getUser(@PathVariable Long id) { + return userService.getUser(id); + } + + @PutMapping("/{id}") + public void updateUser(@PathVariable Long id, @RequestBody UserDto dto){ + userService.updateUser(id, dto); + } + + @DeleteMapping("/{id}") + public void deleteUser(@PathVariable Long id) { + userService.deleteUser(id); + } + +} diff --git "a/week8/\354\234\240\355\232\250\354\204\235/example/dto/CommentDto.java" "b/week8/\354\234\240\355\232\250\354\204\235/example/dto/CommentDto.java" new file mode 100644 index 0000000..27c51c4 --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/example/dto/CommentDto.java" @@ -0,0 +1,29 @@ +package com.example.dto; + +import com.example.entity.Comment; +import com.example.entity.User; +import com.example.entity.Post; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CommentDto { + + private Long id; + private Long postId; + private Long authorId; + private String content; + + //DTO -> entity + // entity -> DTO + + public Comment toEntity(User author, Post post){ + return Comment.builder().content(this.content).author(author).post(post).build(); + } +} diff --git "a/week8/\354\234\240\355\232\250\354\204\235/example/dto/PostDto.java" "b/week8/\354\234\240\355\232\250\354\204\235/example/dto/PostDto.java" new file mode 100644 index 0000000..00bf24c --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/example/dto/PostDto.java" @@ -0,0 +1,29 @@ +package com.example.dto; + +import lombok.*; + +import com.example.entity.Post; +import com.example.entity.User; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PostDto { + //기능 = DTO -> entity + // = entity -> DTO + + private Long id; // post 자체의 id + private Long authorId; // author의 id가 더 필요함. 삭제/수정 권한 때문에 + private String title; + private String content; + + public Post toEntity(User author) { + return Post.builder().title(title).content(content).author(author).build(); + } + + public static PostDto fromEntity(Post post) { + return PostDto.builder().id(post.getId()) + .authorId(post.getAuthor().getId()).title(post.getTitle()).content(post.getContent()).build(); } +} + diff --git "a/week8/\354\234\240\355\232\250\354\204\235/example/dto/UserDto.java" "b/week8/\354\234\240\355\232\250\354\204\235/example/dto/UserDto.java" new file mode 100644 index 0000000..f4c013a --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/example/dto/UserDto.java" @@ -0,0 +1,33 @@ +package com.example.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import com.example.entity.User; + +@Getter +@Builder +@AllArgsConstructor // 생성자를 자동으로 만들어주는 것. +@NoArgsConstructor + +public class UserDto { // data를 front-end로 전달 + + private Long id; + private String name; + private String email; + + + public User toEntity(){ + + User user = User.builder().id(id).name(name).email(email).build(); + return user; + } + + public static UserDto from(User user){ + return UserDto.builder().id(user.getId()).name(user.getName()) + .email(user.getEmail()).build(); + } + + +} diff --git "a/week8/\354\234\240\355\232\250\354\204\235/example/entity/Comment.java" "b/week8/\354\234\240\355\232\250\354\204\235/example/entity/Comment.java" new file mode 100644 index 0000000..5ae4351 --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/example/entity/Comment.java" @@ -0,0 +1,33 @@ +package com.example.entity; + +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@Builder + +public class Comment { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String content; + + @ManyToOne(fetch = FetchType.LAZY) // 댓글 입장에선 author이 다대일 관계임. + private User author; + + @ManyToOne(fetch = FetchType.LAZY) + private Post post; + + public void update(String content){ + this.content = content; + } + + public void setPost(Post post){ + this.post = post; + } + +} diff --git "a/week8/\354\234\240\355\232\250\354\204\235/example/entity/Post.java" "b/week8/\354\234\240\355\232\250\354\204\235/example/entity/Post.java" new file mode 100644 index 0000000..d7a3e0d --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/example/entity/Post.java" @@ -0,0 +1,47 @@ +package com.example.entity; + +import jakarta.persistence.*; +import lombok.*; +import com.example.entity.Comment; + +// import java.util.List; +import java.util.ArrayList; + +import java.util.*; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@Builder + +public class Post { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) // id가 설정이 안될 때 자동으로 값을 넣어줌! + private long id; + + private String title; + private String content; + + @ManyToOne(fetch = FetchType.LAZY) // 다중성 관계 설정 게시판 입장에선 user와 다대일 + private User author; // user를 작성자로 받아온다. + + @OneToMany(fetch = FetchType.LAZY) // 게시판 입장에선 comment와 일대다 + private List comments = new ArrayList<>(); + + public void update(String title, String content){ + this.title = title; + this.content = content; + } + + public void addComment(Comment comment){ + comments.add(comment); + comment.setPost(this); + } + + public void removeComment(Comment comment){ + comments.remove(comment); + comment.setPost(null); + } +} diff --git "a/week8/\354\234\240\355\232\250\354\204\235/example/entity/User.java" "b/week8/\354\234\240\355\232\250\354\204\235/example/entity/User.java" new file mode 100644 index 0000000..794caae --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/example/entity/User.java" @@ -0,0 +1,33 @@ +package com.example.entity; + +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Table(name = "users") +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) // 생성자 -> protected로 생성 +@AllArgsConstructor +@Builder + +public class User { + + @Id// primary key = id다 설정 + @GeneratedValue(strategy = GenerationType.IDENTITY) // id가 안들어오면 자동으로 넣어줘. + private Long id; + + @Enumerated(EnumType.STRING) + private UserRole userRole; + private String name; + private String email; + + public void update(String name, String email) { + this.name = name; + this.email = email; + } + + public boolean isAdmin() { + return userRole == UserRole.ADMIN; + } + +} diff --git "a/week8/\354\234\240\355\232\250\354\204\235/example/entity/UserRole.java" "b/week8/\354\234\240\355\232\250\354\204\235/example/entity/UserRole.java" new file mode 100644 index 0000000..0ce43f0 --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/example/entity/UserRole.java" @@ -0,0 +1,5 @@ +package com.example.entity; + +public enum UserRole { + USER, ADMIN +} diff --git "a/week8/\354\234\240\355\232\250\354\204\235/example/repository/CommentRepository.java" "b/week8/\354\234\240\355\232\250\354\204\235/example/repository/CommentRepository.java" new file mode 100644 index 0000000..78da066 --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/example/repository/CommentRepository.java" @@ -0,0 +1,36 @@ +package com.example.repository; + +import com.example.entity.Comment; + +import java.util.List; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; +import jakarta.persistence.*; +import org.springframework.stereotype.Repository; + +@Repository +@RequiredArgsConstructor + +public class CommentRepository { + // comment들을 가지고 있는 entityManager을 통해 각 entity에 접근한다. + + private final EntityManager entityManager; + + public void save(Comment comment){ + entityManager.persist(comment); + } + + public Comment findById(Long id){ + return entityManager.find(Comment.class, id); + } + + public List findAllCommentsByPostId(Long postId){ + return entityManager.createQuery("SELECT c FROM Comment c" + "WHERE c.post.id = :postId " + "ORDER BY c.id ASC", Comment.class). + setParameter("postId", postId).getResultList(); + } + + public void delete(Comment comment){ + entityManager.remove(comment); + } +} diff --git "a/week8/\354\234\240\355\232\250\354\204\235/example/repository/PostRepository.java" "b/week8/\354\234\240\355\232\250\354\204\235/example/repository/PostRepository.java" new file mode 100644 index 0000000..5a5529c --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/example/repository/PostRepository.java" @@ -0,0 +1,31 @@ +package com.example.repository; + +import com.example.entity.Post; +import jakarta.persistence.*; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +@RequiredArgsConstructor +public class PostRepository { + private final EntityManager entityManager; + + public void save(Post post) { + entityManager.persist(post); + } + + public Post findById(Long id) { + return entityManager.find(Post.class, id); + } + + public List findAll() { + return entityManager.createQuery("SELECT p FROM Post p ORDER BY p.id DESC", Post.class).getResultList(); + } + + public void delete(Post post) { + entityManager.remove(post); + } + +} diff --git "a/week8/\354\234\240\355\232\250\354\204\235/example/repository/UserRepository.java" "b/week8/\354\234\240\355\232\250\354\204\235/example/repository/UserRepository.java" new file mode 100644 index 0000000..f81dc24 --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/example/repository/UserRepository.java" @@ -0,0 +1,36 @@ +package com.example.repository; + +import com.example.entity.User; +import jakarta.persistence.EntityManager; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +@RequiredArgsConstructor +public class UserRepository { + + private final EntityManager em; + + // 저장 + public void save(User user) { + em.persist(user); // INSERT 자동 실행 + } + + // 전체 찾기 + public List findAll() { + return em.createQuery("SELECT u FROM User u", User.class) + .getResultList(); + } + + //하나의 객체만 id로 찾기 + public User findById(Long id) { + return em.find(User.class, id); + } + + public void delete(User user) { + em.remove(user); + } + +} diff --git "a/week8/\354\234\240\355\232\250\354\204\235/example/service/CommentService.java" "b/week8/\354\234\240\355\232\250\354\204\235/example/service/CommentService.java" new file mode 100644 index 0000000..ffe4514 --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/example/service/CommentService.java" @@ -0,0 +1,92 @@ +package com.example.service; + +import com.example.dto.CommentDto; +import com.example.entity.Comment; +import com.example.entity.Post; +import com.example.entity.User; +import com.example.entity.UserRole; +import com.example.repository.CommentRepository; +import com.example.repository.PostRepository; +import com.example.repository.PostRepository; + +import com.example.repository.UserRepository; +import jakarta.persistence.*; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@RequiredArgsConstructor +@Transactional +public class CommentService { + private final CommentRepository commentRepository; + private final PostRepository postRepository; + private final UserRepository userRepository; + + @Transactional + public Long createComment(CommentDto commentDto){ + Post post = postRepository.findById(commentDto.getPostId()); + if(post == null){ + throw new EntityNotFoundException("Post not found"); + } + + User author = userRepository.findById(commentDto.getAuthorId()); + if(author == null){ + throw new EntityNotFoundException("Author not found"); + } + + // post / user 둘 다 실존함. + + Comment comment = commentDto.toEntity(author, post); // comment 생성 + + commentRepository.save(comment); + + return comment.getId(); + } + + @Transactional + public void updateComment(Long commentId, Long requesterId, String newContent){ + Comment comment = commentRepository.findById(commentId); + if(comment == null){ + throw new EntityNotFoundException("Comment not found"); + } + + //comment실존한다면, + + User author = userRepository.findById(requesterId); + if(author == null){ + throw new EntityNotFoundException("Requester not found"); + } + + // author 실존한다면, + + if(author.isAdmin() || author.getId() == comment.getAuthor().getId()){ + comment.update(newContent); + } + else{ + throw new EntityNotFoundException("No authority to update"); + } + } + + @Transactional + public void deleteComment(Long commentId, Long requesterId){ + Comment comment = commentRepository.findById(commentId); + if(comment == null){ + throw new EntityNotFoundException("Comment not found"); + } + + User author = userRepository.findById(requesterId); + if(author == null){ + throw new EntityNotFoundException("Requester not found"); + } + + if(author.isAdmin() || author.getId() == comment.getAuthor().getId()){ + commentRepository.delete(comment); + } + else{ + throw new EntityNotFoundException("No authority to delete"); + } + } +} diff --git "a/week8/\354\234\240\355\232\250\354\204\235/example/service/PostService.java" "b/week8/\354\234\240\355\232\250\354\204\235/example/service/PostService.java" new file mode 100644 index 0000000..c5e636d --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/example/service/PostService.java" @@ -0,0 +1,79 @@ +package com.example.service; + +import com.example.entity.*; +import com.example.repository.*; +import com.example.dto.PostDto; +import jakarta.persistence.*; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@RequiredArgsConstructor +@Transactional() +public class PostService { + private final PostRepository postRepository; + private final UserRepository userRepository; + + @Transactional + public Long createPost(PostDto postDto) { + //post를 생성하고 생성된 postID를 return 해준다. + + //1. 작성자 존재 유무 파악 + //2. 포스트 생성 + //3. 포스트 정장 + //4. 포스트 아이디 return + + User author = userRepository.findById(postDto.getAuthorId()); + + if(author == null) { + throw new EntityNotFoundException("User not found"); // 작성자를 찾을 수 없다. + } + + Post post = postDto.toEntity(author); + postRepository.save(post); + + postRepository.save(post); + + return post.getId(); + } + + public List findAllPosts() { + return postRepository.findAll(); + } + + public Post getPostById(Long postId) { + Post post = postRepository.findById(postId); + + if(post == null) { + throw new EntityNotFoundException("Post not found"); // 찾으려는 postid가 존재하지 않는다. + } + + return post; + } + @Transactional + public void deletePost(Long postId, Long userId) { + // 게시글 유무 파악 -> 있다면 author 권한check (admin인지 check -> author id를 비교) -> 권한 ok일 때 삭제 + + Post post = getPostById(postId); + if(post == null) { + throw new EntityNotFoundException("Post not found"); + } + + User user = userRepository.findById(userId); + if(user == null) { + throw new EntityNotFoundException("User not found"); + } + + if(post.getAuthor().equals(user) || post.getAuthor().isAdmin()) { + postRepository.delete(post); + } + else{ + throw new IllegalArgumentException("No authority to delete"); + } + } + +} diff --git "a/week8/\354\234\240\355\232\250\354\204\235/example/service/UserService.java" "b/week8/\354\234\240\355\232\250\354\204\235/example/service/UserService.java" new file mode 100644 index 0000000..0278434 --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/example/service/UserService.java" @@ -0,0 +1,48 @@ +package com.example.service; + +import com.example.entity.User; +import com.example.dto.UserDto; +import com.example.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor // 생성자 주입을 자동으로 해줌. private finally를 대신 입력해야 함. // autowired를 하면 그래도 생성자까진 만들어야 했는데 얘 하면 생성자 없이 가능함. 생성자 만들면 안됨. +@Transactional +public class UserService { + private final UserRepository userRepository; + + public void addUser(UserDto userDto) { + // DTO -> Entity -> Entity로 저장 + User user = userDto.toEntity(); // entity변환 + + userRepository.save(user); // entity 저장 + } + + public List getAllUsers(){ + + return userRepository.findAll().stream().map(UserDto::from).collect(Collectors.toList()); + } +//repository한테 받아! + + public UserDto getUser(Long id) { + User user = userRepository.findById(id); + return UserDto.from(user); + } + + @Transactional + public void updateUser(Long id, UserDto userDto) { + User user = userRepository.findById(id); + user.update(userDto.getName(), userDto.getEmail()); + } + + @Transactional + public void deleteUser(Long id) { + User user = userRepository.findById(id); + userRepository.delete(user); + } +} diff --git "a/week8/\354\234\240\355\232\250\354\204\235/repository/CommentRepository.java" "b/week8/\354\234\240\355\232\250\354\204\235/repository/CommentRepository.java" new file mode 100644 index 0000000..78da066 --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/repository/CommentRepository.java" @@ -0,0 +1,36 @@ +package com.example.repository; + +import com.example.entity.Comment; + +import java.util.List; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; +import jakarta.persistence.*; +import org.springframework.stereotype.Repository; + +@Repository +@RequiredArgsConstructor + +public class CommentRepository { + // comment들을 가지고 있는 entityManager을 통해 각 entity에 접근한다. + + private final EntityManager entityManager; + + public void save(Comment comment){ + entityManager.persist(comment); + } + + public Comment findById(Long id){ + return entityManager.find(Comment.class, id); + } + + public List findAllCommentsByPostId(Long postId){ + return entityManager.createQuery("SELECT c FROM Comment c" + "WHERE c.post.id = :postId " + "ORDER BY c.id ASC", Comment.class). + setParameter("postId", postId).getResultList(); + } + + public void delete(Comment comment){ + entityManager.remove(comment); + } +} diff --git "a/week8/\354\234\240\355\232\250\354\204\235/repository/PostRepository.java" "b/week8/\354\234\240\355\232\250\354\204\235/repository/PostRepository.java" new file mode 100644 index 0000000..5a5529c --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/repository/PostRepository.java" @@ -0,0 +1,31 @@ +package com.example.repository; + +import com.example.entity.Post; +import jakarta.persistence.*; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +@RequiredArgsConstructor +public class PostRepository { + private final EntityManager entityManager; + + public void save(Post post) { + entityManager.persist(post); + } + + public Post findById(Long id) { + return entityManager.find(Post.class, id); + } + + public List findAll() { + return entityManager.createQuery("SELECT p FROM Post p ORDER BY p.id DESC", Post.class).getResultList(); + } + + public void delete(Post post) { + entityManager.remove(post); + } + +} diff --git "a/week8/\354\234\240\355\232\250\354\204\235/repository/UserRepository.java" "b/week8/\354\234\240\355\232\250\354\204\235/repository/UserRepository.java" new file mode 100644 index 0000000..f81dc24 --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/repository/UserRepository.java" @@ -0,0 +1,36 @@ +package com.example.repository; + +import com.example.entity.User; +import jakarta.persistence.EntityManager; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +@RequiredArgsConstructor +public class UserRepository { + + private final EntityManager em; + + // 저장 + public void save(User user) { + em.persist(user); // INSERT 자동 실행 + } + + // 전체 찾기 + public List findAll() { + return em.createQuery("SELECT u FROM User u", User.class) + .getResultList(); + } + + //하나의 객체만 id로 찾기 + public User findById(Long id) { + return em.find(User.class, id); + } + + public void delete(User user) { + em.remove(user); + } + +} diff --git "a/week8/\354\234\240\355\232\250\354\204\235/service/CommentService.java" "b/week8/\354\234\240\355\232\250\354\204\235/service/CommentService.java" new file mode 100644 index 0000000..ffe4514 --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/service/CommentService.java" @@ -0,0 +1,92 @@ +package com.example.service; + +import com.example.dto.CommentDto; +import com.example.entity.Comment; +import com.example.entity.Post; +import com.example.entity.User; +import com.example.entity.UserRole; +import com.example.repository.CommentRepository; +import com.example.repository.PostRepository; +import com.example.repository.PostRepository; + +import com.example.repository.UserRepository; +import jakarta.persistence.*; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@RequiredArgsConstructor +@Transactional +public class CommentService { + private final CommentRepository commentRepository; + private final PostRepository postRepository; + private final UserRepository userRepository; + + @Transactional + public Long createComment(CommentDto commentDto){ + Post post = postRepository.findById(commentDto.getPostId()); + if(post == null){ + throw new EntityNotFoundException("Post not found"); + } + + User author = userRepository.findById(commentDto.getAuthorId()); + if(author == null){ + throw new EntityNotFoundException("Author not found"); + } + + // post / user 둘 다 실존함. + + Comment comment = commentDto.toEntity(author, post); // comment 생성 + + commentRepository.save(comment); + + return comment.getId(); + } + + @Transactional + public void updateComment(Long commentId, Long requesterId, String newContent){ + Comment comment = commentRepository.findById(commentId); + if(comment == null){ + throw new EntityNotFoundException("Comment not found"); + } + + //comment실존한다면, + + User author = userRepository.findById(requesterId); + if(author == null){ + throw new EntityNotFoundException("Requester not found"); + } + + // author 실존한다면, + + if(author.isAdmin() || author.getId() == comment.getAuthor().getId()){ + comment.update(newContent); + } + else{ + throw new EntityNotFoundException("No authority to update"); + } + } + + @Transactional + public void deleteComment(Long commentId, Long requesterId){ + Comment comment = commentRepository.findById(commentId); + if(comment == null){ + throw new EntityNotFoundException("Comment not found"); + } + + User author = userRepository.findById(requesterId); + if(author == null){ + throw new EntityNotFoundException("Requester not found"); + } + + if(author.isAdmin() || author.getId() == comment.getAuthor().getId()){ + commentRepository.delete(comment); + } + else{ + throw new EntityNotFoundException("No authority to delete"); + } + } +} diff --git "a/week8/\354\234\240\355\232\250\354\204\235/service/PostService.java" "b/week8/\354\234\240\355\232\250\354\204\235/service/PostService.java" new file mode 100644 index 0000000..c5e636d --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/service/PostService.java" @@ -0,0 +1,79 @@ +package com.example.service; + +import com.example.entity.*; +import com.example.repository.*; +import com.example.dto.PostDto; +import jakarta.persistence.*; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@RequiredArgsConstructor +@Transactional() +public class PostService { + private final PostRepository postRepository; + private final UserRepository userRepository; + + @Transactional + public Long createPost(PostDto postDto) { + //post를 생성하고 생성된 postID를 return 해준다. + + //1. 작성자 존재 유무 파악 + //2. 포스트 생성 + //3. 포스트 정장 + //4. 포스트 아이디 return + + User author = userRepository.findById(postDto.getAuthorId()); + + if(author == null) { + throw new EntityNotFoundException("User not found"); // 작성자를 찾을 수 없다. + } + + Post post = postDto.toEntity(author); + postRepository.save(post); + + postRepository.save(post); + + return post.getId(); + } + + public List findAllPosts() { + return postRepository.findAll(); + } + + public Post getPostById(Long postId) { + Post post = postRepository.findById(postId); + + if(post == null) { + throw new EntityNotFoundException("Post not found"); // 찾으려는 postid가 존재하지 않는다. + } + + return post; + } + @Transactional + public void deletePost(Long postId, Long userId) { + // 게시글 유무 파악 -> 있다면 author 권한check (admin인지 check -> author id를 비교) -> 권한 ok일 때 삭제 + + Post post = getPostById(postId); + if(post == null) { + throw new EntityNotFoundException("Post not found"); + } + + User user = userRepository.findById(userId); + if(user == null) { + throw new EntityNotFoundException("User not found"); + } + + if(post.getAuthor().equals(user) || post.getAuthor().isAdmin()) { + postRepository.delete(post); + } + else{ + throw new IllegalArgumentException("No authority to delete"); + } + } + +} diff --git "a/week8/\354\234\240\355\232\250\354\204\235/service/UserService.java" "b/week8/\354\234\240\355\232\250\354\204\235/service/UserService.java" new file mode 100644 index 0000000..0278434 --- /dev/null +++ "b/week8/\354\234\240\355\232\250\354\204\235/service/UserService.java" @@ -0,0 +1,48 @@ +package com.example.service; + +import com.example.entity.User; +import com.example.dto.UserDto; +import com.example.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor // 생성자 주입을 자동으로 해줌. private finally를 대신 입력해야 함. // autowired를 하면 그래도 생성자까진 만들어야 했는데 얘 하면 생성자 없이 가능함. 생성자 만들면 안됨. +@Transactional +public class UserService { + private final UserRepository userRepository; + + public void addUser(UserDto userDto) { + // DTO -> Entity -> Entity로 저장 + User user = userDto.toEntity(); // entity변환 + + userRepository.save(user); // entity 저장 + } + + public List getAllUsers(){ + + return userRepository.findAll().stream().map(UserDto::from).collect(Collectors.toList()); + } +//repository한테 받아! + + public UserDto getUser(Long id) { + User user = userRepository.findById(id); + return UserDto.from(user); + } + + @Transactional + public void updateUser(Long id, UserDto userDto) { + User user = userRepository.findById(id); + user.update(userDto.getName(), userDto.getEmail()); + } + + @Transactional + public void deleteUser(Long id) { + User user = userRepository.findById(id); + userRepository.delete(user); + } +}