[REST API 서버 구현] 최정훈 제출합니다.#36
Conversation
Donghwan814
left a comment
There was a problem hiding this comment.
안녕하세요 정훈님! 😊
드디어 Java 과제가 끝나고, Spring Boot를 사용한 첫 과제를 제출하시느라 정말 고생 많으셨습니다!
Spring Boot에서는 @service, @repository, @PathVariable, @RequestBody처럼 @ 어노테이션을 많이 사용하다 보니, Node.js와는 코드 작성 방식이 달라서 처음에는 헷갈리거나 어색하게 느껴지셨을 것 같습니다.
그럼에도 전체적으로 Controller, Service, Repository 계층을 나누어 구조를 깔끔하게 잡아주신 점이 좋았습니다. 처음 Spring Boot를 다루는 과제임에도 기본 CRUD 흐름을 잘 구현해주신 것 같습니다!
이번 코드 리뷰는 단순히 틀린 부분을 지적하기보다는, REST API의 응답 상태 코드, Repository와 Service의 역할, 트랜잭션 처리처럼 앞으로 Spring Boot로 개발할 때 계속 중요하게 다루게 될 개념들을 함께 짚어보는 방향으로 작성했습니다.
리뷰 내용을 보시면서 이해가 잘 되지 않거나 궁금한 부분이 있다면 편하게 말씀해주세요.
이번 과제를 계기로 Spring Boot와 조금 더 친해지는 시간이 되셨으면 좋겠습니다! 😊
|
안녕하세요 정훈님. 첫 스프링 과제임에도 불구하고 기간 내에 성실히 제출해 주셔서 감사합니다. 과제 수행하시느라 고생 많으셨습니다. 이번 리뷰는 저와 동환이가 공동으로 진행합니다. 두 사람이 함께 검토하다 보니 일부 중복되는 피드백이 포함될 수 있는 점 미리 안내해 드립니다. 남겨드린 코멘트를 확인해 보시고, 추가적인 질문이나 의견이 있으시면 언제든지 편하게 답변 남겨주세요. |
There was a problem hiding this comment.
혹시 Postman API 테스트 캡처 이미지를 자바 패키지 내부에 포함하신 특별한 이유가 있을까요?
패키지 내부에는 순수 프로젝트 소스 코드만 관리하는 것이 깔끔해서요! 테스트 결과 같은 이미지 파일은 패키지 외부(예: root 디렉토리의 별도 폴더)에서 관리하거나, PR(Pull Request)을 올리실 때 본문에 첨부해 주시면 리뷰어들이 확인하기에 훨씬 더 좋을 것 같습니다.
There was a problem hiding this comment.
처음 진행해보는 과정이어서 해당 부분까지 제대로 확인하지 못했던 것 같습니다. 말씀하신대로 패키지 내부에는 소스 코드만 관리하는 것이 더 깔끔한 구조인 것 같습니다. 이미지 파일은 별도의 폴더로 분리해 보겠습니다.
There was a problem hiding this comment.
코드를 확인하던 중 application.properties 파일이 깃허브에 함께 올라온 것을 확인했습니다.
해당 파일은 데이터베이스 주소나 외부 API 연동 키, JWT 설정 등 백엔드의 핵심 환경 변수들을 다루는 곳입니다. 이러한 설정 파일이 퍼블릭 저장소에 노출되면 정보 유출의 위험이 커지므로 주의가 필요합니다.
대안으로 민감한 정보를 **.env (환경 변수 파일)**로 분리하고, 해당 파일들이 로컬에서만 동작하도록 .gitignore에 등록하여 관리하는 방식을 추천해 드립니다. 관련 내용을 참고하셔서 설정 파일을 안전하게 가린 뒤 다시 반영해 주시면 감사하겠습니다.
There was a problem hiding this comment.
처음 진행해 보아서 해당 부분을 제대로 고려하지 못했던 것 같습니다. application.properties 파일은 중요한 정보가 저장되므로 공개 저장소에 업로드 되는 부분에 주의하겠습니다. 해당 부분이 커밋에 포함되지 않게 하거나 ,gitignore로 설정하여 수정하도록 하겠습니다.
| public interface PostService { | ||
|
|
||
| public Post save(PostRequest postRequest); | ||
| public Optional<Post> findById(Long id); // null일 경우 Optional이 처리 |
There was a problem hiding this comment.
findById 메서드의 반환 타입으로 Optional을 사용해 주셨습니다.
Optional을 적용하신 이유가 궁금합니다.
There was a problem hiding this comment.
PostService 인터페이스의 findById 반환 타입을 Optional로 정의해 주셨네요!
보통 Repository 계층(PostRepository)은 DB에 데이터가 없을 수 있으므로 Optional을 반환하는 것이 자연스럽지만, Service 계층은 그 Optional을 넘겨받아 데이터 유무를 확인하고 예외 처리(e.g. orElseThrow)까지 마친 뒤, 알맹이(순수 Post 엔티티나 PostResponse DTO)만 깔끔하게 반환하는 역할을 주로 담당합니다.
지금 구조처럼 서비스 메서드에서도 Optional을 그대로 리턴하게 되면, 이 서비스를 호출하는 컨트롤러(Controller) 단에서 또다시 null 체크나 예외 처리를 해야 하는 번거로움이 생길 수 있어요!
혹시 서비스 계층에서 Optional을 그대로 반환하도록 설계하신 정훈님만의 의도가 있으신지 궁금합니다.
There was a problem hiding this comment.
해당 부분은 제가 임의로 구현했다기 보다는 멋쟁이사자처럼의 강의 내용의 코드를 참고하였습니다. 처음 스프링 구조를 학습하는 단계라서 강의 코드에 맞추어서 Repository에서 받은 Optional을 서비스 계층에서 그대로 반환되게 작성하였습니다. 말씀해주신 것처럼 서비스 계층에서 Optional을 넘겨받아 데이터 유무 확인 및 예외 처리 후 순수 객체만 반환하게 수정해 보겠습니다.
|
|
||
| public Post save(PostRequest postRequest); | ||
| public Optional<Post> findById(Long id); // null일 경우 Optional이 처리 | ||
| public List<Post> findAllPosts(); |
There was a problem hiding this comment.
현재 게시글 전체를 List 타입으로 한 번에 조회하도록 구현해 주셨네요! 데이터가 적을 때는 괜찮지만, 만약 게시글이 1만 개, 10만 개로 늘어난다면 어떤 문제가 생길지 혹시 고민해 보셨을까요?
수많은 데이터를 한 번에 List로 받아오면 데이터베이스와 서버 메모리에 엄청난 부하가 걸리고, 클라이언트(프론트엔드)에 전달하는 과정에서도 네트워크 속도가 급격히 느려지게 됩니다. 사용자 경험에도 치명적일 수 있어요.
그래서 실무에서는 데이터를 일정 단위로 끊어서 가져오는 페이징(Paging) 처리를 필수로 적용합니다. 스프링 데이터 JPA가 제공하는 Slice와 Page 인터페이스에 대해 공부해 보시고, 현재 프로젝트 스펙(무한 스크롤 방식인지, 페이지 번호 방식인지)에 맞는 방식을 골라 적용해 보시면 백엔드 개발자로서 한 단계 더 성장하실 수 있을 것 같습니다!
There was a problem hiding this comment.
해당 기능을 처음 구현해봐서 기능이 동작하는지에 집중해서 대용량 데이터를 처리하는 상황까지는 신경쓰지 못했던 것 같습니다. 말씀해 주신 대로 스프링 데이터 JPA가 제공하는 Slice와 Page 인터페이스에 대해서 좀 더 공부해 보겠습니다.
| import java.util.List; | ||
| import java.util.Optional; | ||
|
|
||
| @CrossOrigin("*") |
There was a problem hiding this comment.
혹시 이번 CRUD 과제에서 @crossorigin 어노테이션을 통해 CORS 설정을 추가하신 특별한 이유가 있으실까요?
아시다시피 CORS는 **'웹 브라우저'**가 서로 다른 도메인 간의 리소스 요청을 제한하는 보안 정책입니다. 지금처럼 로컬 환경에서 프론트엔드 연동 없이 Postman 같은 API 클라이언트로만 테스트할 때는 브라우저의 제약을 받지 않기 때문에 CORS 설정이 없어도 정상적으로 호출이 가능한데요!
혹시 나중에 프론트엔드와의 협업이나 배포를 염두에 두고 미리 연습 삼아 넣어두신 것인지, 아니면 다른 이유가 있었는지 정훈님의 의도가 궁금합니다. 이번 기회에 CORS가 정확히 어느 시점에, 왜 발생하는지 다시 한번 정리해 보시면 백엔드 면접 대비에도 큰 도움이 될 것 같습니다!
There was a problem hiding this comment.
해당 부분 또한 제가 필요성을 느껴서 작성했다기보다는 멋쟁이사자처럼 강의에서 사용한 코드를 참고하면서 작성한 내용입니다. 말씀해 주신 대로 현재 Postman으로 테스트를 진행하면서 굳이 CORS 설정이 필요한 상황은 아니었던 것 같습니다. CORS 설정과 @crossorigin 어노테이션이 어떤 기능을 가지고 어떤 역할을 하는지에 대해서 더 알아보도록 하겠습니다.
| @RequestMapping("/api") | ||
| public class PostController { | ||
|
|
||
| @Autowired |
There was a problem hiding this comment.
정훈님, 이번 과제에서 의존성 주입을 위해 @Autowired를 활용하신 구체적인 배경이 궁금합니다.
지난 동아리 시간에 제가 말씀드렸던 @Autowired와 @requiredargsconstructor는 스프링에서 빈(Bean)을 주입받는 대표적인 두 가지 방법인데요. 두 어노테이션의 기능적 차이나 각각의 장단점을 고려하여 코드를 작성하신 것인지 정훈님의 생각이 궁금합니다.
There was a problem hiding this comment.
아직 @requiredargsconstructor를 직접 사용해본 경험이 많지 않아서 기존에 더 익숙했던 @Autowired 방식으로 구현해 보았습니다. 나중에 보니 @Autowired는 객체를 필드에 직접 주입하는 방식이고, @requiredargsconstructor는 생성자를 통해서 의존성을 주입하는 방식이라는 차이점에 대해서 알게 되었습니다. 두 방식에 대해서 좀 더 구체적으로 어떤 차이인지 이해해보도록 하겠습니다.
| private String title; | ||
| private String content; | ||
|
|
||
| public String getTitle() { |
There was a problem hiding this comment.
| import java.util.List; | ||
| import java.util.Optional; | ||
|
|
||
| @CrossOrigin("*") |
There was a problem hiding this comment.
@crossorigin이라는 어노테이션은 저도 처음 보는 어노테이션이네요!
제가 검색해서 찾아봤을 때는 "웹 페이지의 제한된 자원을 외부 도메인에서 접근을 허용해주는 매커니즘"이라고 나오는데
혹시, 이 어노테이션을 사용하신 이유는 무엇인지 정훈님의 의견을 들어보고 싶습니다!
There was a problem hiding this comment.
해당 부분은 제가 멋쟁이사자처럼 강의를 보고 강의에서 사용된 코드를 참고하면서 작성한 내용입니다. 위에서 말씀드린 것 처럼 Postman으로 테스트를 진행하면서 굳이 이 설정이 필요한 상황은 아니었던 것 같습니다. @crossorigin 어노테이션에 대해서 어떤 기능을 가지고 어떤 역할을 하는지 더 알아보겠습니다.
There was a problem hiding this comment.
강의를 참고해서 어노테이션을 활용해주신 부분은 좋은 시도라고 생각합니다!
다만 어노테이션을 적용하기 전, 해당 어노테이션이 어떤 역할을 하고 어떤 상황에서 필요한지 의미를 명확히 이해한 뒤 사용하는 것이 더 좋을 것 같습니다.
단순히 강의나 예제에서 사용했다는 이유로 그대로 따라 적용하기보다는, 현재 코드 상황에 실제로 필요한 설정인지 판단하는 과정이 중요하다고 생각합니다.
테스트를 진행하면서 이 설정이 굳이 필요하지 않은 상황이라고 판단하신 것 같은데, 혹시 그렇게 생각하신 이유가 있을까요?
만약 현재 로직에서 불필요한 설정이라면 제거하거나, 반대로 필요한 설정이라면 왜 필요한지 주석이나 설명으로 남겨주시면 더 좋을 것 같습니다!
| @RequestMapping("/api") | ||
| public class PostController { | ||
|
|
||
| @Autowired |
There was a problem hiding this comment.
@Autowired를 사용해서 PostServiceImpl을 Controller에 주입해주신 부분 확인했습니다!
Spring Boot를 처음 접하면 의존성 주입 방식이 익숙하지 않을 수 있는데, Controller와 Service 계층을 분리하고 Service를 주입해서 사용하려고 하신 점은 좋은 방향이라고 생각합니다.
다만 한 가지 같이 고민해보면 좋을 부분이 있습니다.
@Autowired는 Spring이 필요한 객체를 자동으로 주입해주는 의존성 주입 어노테이션인데, 보통은 필드 주입 방식보다 생성자 주입 방식을 많이 사용하는 편입니다.
예를 들어 저는 보통 아래처럼 private final과 @requiredargsconstructor를 함께 사용해서 의존성을 주입하는 방식을 선호합니다.
@RequiredArgsConstructor
@RestController
public class PostController {
private final PostService postService;
}
이 방식은 의존성이 명확하게 드러나고, 객체가 생성될 때 필요한 값이 반드시 주입되기 때문에 테스트나 유지보수 측면에서도 장점이 있다고 생각합니다.
현재 작성해주신 방식이 틀렸다는 의미는 아니고, 혹시 @Autowired를 사용하신 이유가 따로 있으셨는지 궁금합니다!
처음 학습 과정에서 예제를 참고하신 방식인지, 아니면 의도적으로 필드 주입 방식을 선택하신 것인지 의견을 들어보고 싶습니다.
There was a problem hiding this comment.
@requiredargconstructor를 사용해본 경험이 그다지 없어서 기존에 좀 더 익숙한 방식인 @Autowired 방식을 사용하였습니다. @Autowired는 객체를 필드에 직접 주입하는 방식이고, @requiredargconstructor는 생성자를 통해서 의존성을 주입하는 방식이라는 점을 알게되었습니다. 두 방식의 차이점에 대해서 좀 더 구체적으로 이해해보겠습니다.
There was a problem hiding this comment.
정훈님이 사용하신 방식도 크게 문제가 되는 방식은 아니라고 생각합니다!
제가 말씀드린 내용도 무조건적인 정답이라기보다는, “이런 관점에서도 생각해볼 수 있지 않을까?”라는 의미로 봐주시면 좋을 것 같습니다.
이번 과제 코드리뷰의 중점은 단순히 맞고 틀림을 판단하는 것이 아니라, 각자 작성한 코드 방식에 대해 왜 그렇게 구현했는지 생각을 공유하고, 더 나은 방향을 함께 고민해보는 데 있다고 생각합니다.
그래서 제가 드린 피드백도 정답을 강요하려는 의도는 아니고, 정훈님 코드에 대해 한 번 더 고민해볼 수 있는 관점으로 봐주시면 감사하겠습니다!
|
|
||
| return null; | ||
| } | ||
| } |
There was a problem hiding this comment.
try-catch문을 사용해서 예외 상황을 처리하려고 하신 점은 좋았습니다!
Controller에서 발생할 수 있는 예외를 고려하고, 단순히 정상 흐름만 작성하지 않으신 부분은 좋은 방향이라고 생각합니다.
다만 한 가지 같이 고민해보면 좋을 부분이 있습니다.
현재 대부분의 메서드에서 예외가 발생했을 때 마지막에 return null;을 반환하고 있는데, 이 경우 클라이언트 입장에서는 어떤 상황에서 예외가 발생했는지 확인하기 어렵고, 서버 응답도 명확하지 않을 수 있을 것 같습니다.
catch (Exception e) {
e.printStackTrace();
}
return null;
특히 ResponseEntity를 사용하는 이유는 요청 처리 결과를 상태 코드와 함께 명확하게 반환하기 위해서라고 생각합니다.
그래서 예외가 발생했을 때 null을 반환하기보다는 우선 아래처럼 명확한 상태 코드를 반환하는 방식이 더 좋을 것 같습니다.
return ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.build();
또한 삭제 API처럼 응답 Body가 필요 없는 경우에는 null을 사용하기보다는 ResponseEntity를 사용하는 방식이 더 의도가 명확해 보입니다.
public ResponseEntity<Void> deletePost(@PathVariable("id") long id) {
postService.delete(id);
return ResponseEntity.noContent().build();
}
추후에는 GlobalExceptionHandler를 만들어서 예외 처리를 Controller마다 반복하지 않고 한 곳에서 관리하면, 코드도 더 깔끔해지고 클라이언트에게도 404 Not Found, 400 Bad Request, 500 Internal Server Error처럼 상황에 맞는 응답을 더 명확하게 전달할 수 있을 것 같습니다.
There was a problem hiding this comment.
말씀하신 것 처럼 ResponseEntity를 사용하는 이유는 단순히 데이터를 반환하기 위한 것이 아니라 요청 처리 결과를 명확하게 반환하기 위한 것이라는 것에 대해서 알게 되었습니다.
일단 예외 발생 시에는 'return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();'처럼 명확한 상태 코드로 반환하도록 수정해보겠습니다. 그리고 삭제 API처럼 응답 Body가 필요 없는 경우에는 ResponseEntity와 noContent()(를 사용해보겠습니다.
|
|
||
| import java.time.LocalDateTime; | ||
|
|
||
| public class PostRequest { |
There was a problem hiding this comment.
자바 클래스 타입중에 record라는 타입이 있습니다! 간단하게 소개하자면,
record 타입을 사용하면 객체 의도를 좀 더 명확하게 표현할 수 있고 가독성이 좋아진다는 측면에서
DTO에서 많이 사용하는 방식입니다!
이 링크를 통해 한 번 살펴보시면 많은 도움이 될 것입니다!
https://velog.io/@hyeok_1212/Java-Record-%EC%82%AC%EC%9A%A9%ED%95%98%EC%8B%9C%EB%82%98%EC%9A%94
There was a problem hiding this comment.
말씀해주신 대로 record 타입에 대해서 알아보고 해당 코드를 record 타입으로 어떻게 변환해보면 좋을지 생각해보도록 하겠습니다.
|
|
||
| public String getContent() { | ||
| return content; | ||
| } |
There was a problem hiding this comment.
Getter/Setter 메서드를 직접 작성해주신 부분 확인했습니다!
필드 값을 안전하게 접근하고 수정할 수 있도록 메서드를 따로 만들어주신 점은 좋은 방향이라고 생각합니다.
다만 Spring Boot에서는 보통 Lombok 라이브러리에서 제공하는 @Getter, @Setter 어노테이션을 활용해서 Getter/Setter 코드를 자동으로 생성할 수 있습니다.
현재처럼 직접 모두 작성해도 동작에는 문제가 없지만, 필드가 많아질수록 코드가 길어지고 가독성이 조금 떨어질 수 있을 것 같습니다.
예를 들어 아래처럼 작성하면,
@Getter
@Setter
public class PostRequest {
private String title;
private String content;
}
직접 getTitle(), setTitle() 같은 메서드를 작성하지 않아도 되기 때문에 코드가 훨씬 깔끔해지고 핵심 로직에 더 집중할 수 있을 것 같습니다.
그래서 이후에는 반복되는 Getter/Setter 작성이 필요한 클래스에서는 Lombok의 @Getter, @Setter를 한 번 활용해보셔도 좋을 것 같습니다!
| public class Post { | ||
|
|
||
| @Id | ||
| @GeneratedValue(strategy= GenerationType.AUTO) |
There was a problem hiding this comment.
@GeneratedValue를 사용해서 PK 값을 자동 생성하도록 설정해주신 점 좋았습니다!
다만 현재 GenerationType.AUTO를 사용하고 있는데, AUTO는 JPA가 DB에 맞는 전략을 자동으로 선택하는 방식이고, IDENTITY는 DB의 자동 증가 기능을 직접 사용하는 방식으로 알고 있습니다.
혹시 정훈님께서 AUTO를 선택하신 특별한 이유가 있으셨는지 궁금해서 질문을 드리고 싶습니다!
There was a problem hiding this comment.
해당 부분 또한 멋쟁이사자처럼의 강의 코드를 참고하면서 작성한 내용입니다. @GeneratedValue의 AUTO는 JPA가 DB에 맞는 전략을 자동으로 선택하는 방식이고, IDENTITY는 DB의 자동 증가 기능을 직접 사용하는 방식이라는 점을 알게 되었습니다. 이 과제에서는 AUTO 보다는 IDENTITY가 좀 더 직관적이기에 어울리는 것 같습니다. AUTO에서 IDENTITY로 수정하도록 하겠습니다.
There was a problem hiding this comment.
이 부분 또한 강의를 참고해서 작성해주신 점은 좋은 시도라고 생각합니다!
다만 제가 말씀드리는 방식이 무조건적인 정답은 아닙니다. 정훈님께서 강의를 참고해서 해당 방식으로 작성해주셨다면, 단순히 예제를 따라 적용하기보다는 왜 이 방식을 사용하는지, 그리고 현재 코드에서 어떤 역할을 하는지를 이해한 뒤 적용하는 것이 더 좋을 것 같습니다.
자료를 참고해서 코드를 작성하는 것은 매우 좋은 자세라고 생각합니다. 다만 다음에는 참고한 내용을 그대로 적용하기보다는, 해당 방식이 필요한 이유와 적용 위치를 충분히 이해한 뒤 사용하시면 더 완성도 있는 코드가 될 것 같습니다!
이번 기회에 해당 부분이 어떤 역할을 하는지 한 번 더 정리해보시면 좋을 것 같습니다.
| import org.springframework.stereotype.Repository; | ||
|
|
||
| @Repository | ||
| public interface PostRepository extends JpaRepository<Post, Long> { |
There was a problem hiding this comment.
Repository 구조를 깔끔하게 분리해서 작성해주신 점 좋았습니다!
Controller나 Service에서 DB 접근 로직을 직접 처리하지 않고, Repository 계층을 따로 두신 부분은 유지보수 측면에서도 좋은 설계라고 생각합니다.
다만 한 가지 궁금한 점이 있습니다.
Repository의 역할은 우선 무엇일까요?
현재 게시글을 조회하거나 삭제할 때 id 값에 해당하는 게시글이 실제로 존재하는지 확인하는 로직이나, 필요한 경우 중복 데이터를 확인하는 로직은 아직 따로 작성되지 않은 것으로 보였습니다.
예를 들어 존재하지 않는 id로 조회하거나 삭제 요청이 들어왔을 때 어떤 응답을 반환할지에 대한 처리가 필요할 수 있을 것 같습니다.
There was a problem hiding this comment.
현재는 JpaRepository를 상속받아 기본적인 CRUD 기능을 사용하는 형태로 구현하였는데, 말씀해주신 대로 Repository는 DB를 연결하는 것 뿐만 아니라 데이터의 존재 여부나 중복 데이터를 확인하는 역할도 수행해야 한다는 사실을 알게 되었습니다. 이후 'findById()'를 사용해서 게시글의 존재 여부를 확인하고 상황에 맞추어 응답하도록 수정해보겠습니다.
There was a problem hiding this comment.
수정 완료해주시면 확인할 수 있도록 연락 부탁드립니다!
이번 코드리뷰가 단순히 코드를 수정하는 과정에서 끝나는 것이 아니라, 서로 잘 몰랐던 부분을 이해하고 실제 코드에 적용해보면서 한 단계씩 성장할 수 있는 시간이 되었으면 좋겠습니다.
정훈님도 이번 피드백을 너무 부담스럽게 받아들이기보다는, 더 나은 방향을 함께 고민해보는 과정으로 생각해주시면 감사하겠습니다!
| import java.util.List; | ||
| import java.util.Optional; | ||
|
|
||
| @CrossOrigin("*") |
There was a problem hiding this comment.
강의를 참고해서 어노테이션을 활용해주신 부분은 좋은 시도라고 생각합니다!
다만 어노테이션을 적용하기 전, 해당 어노테이션이 어떤 역할을 하고 어떤 상황에서 필요한지 의미를 명확히 이해한 뒤 사용하는 것이 더 좋을 것 같습니다.
단순히 강의나 예제에서 사용했다는 이유로 그대로 따라 적용하기보다는, 현재 코드 상황에 실제로 필요한 설정인지 판단하는 과정이 중요하다고 생각합니다.
테스트를 진행하면서 이 설정이 굳이 필요하지 않은 상황이라고 판단하신 것 같은데, 혹시 그렇게 생각하신 이유가 있을까요?
만약 현재 로직에서 불필요한 설정이라면 제거하거나, 반대로 필요한 설정이라면 왜 필요한지 주석이나 설명으로 남겨주시면 더 좋을 것 같습니다!
| @RequestMapping("/api") | ||
| public class PostController { | ||
|
|
||
| @Autowired |
There was a problem hiding this comment.
정훈님이 사용하신 방식도 크게 문제가 되는 방식은 아니라고 생각합니다!
제가 말씀드린 내용도 무조건적인 정답이라기보다는, “이런 관점에서도 생각해볼 수 있지 않을까?”라는 의미로 봐주시면 좋을 것 같습니다.
이번 과제 코드리뷰의 중점은 단순히 맞고 틀림을 판단하는 것이 아니라, 각자 작성한 코드 방식에 대해 왜 그렇게 구현했는지 생각을 공유하고, 더 나은 방향을 함께 고민해보는 데 있다고 생각합니다.
그래서 제가 드린 피드백도 정답을 강요하려는 의도는 아니고, 정훈님 코드에 대해 한 번 더 고민해볼 수 있는 관점으로 봐주시면 감사하겠습니다!
| public class Post { | ||
|
|
||
| @Id | ||
| @GeneratedValue(strategy= GenerationType.AUTO) |
There was a problem hiding this comment.
이 부분 또한 강의를 참고해서 작성해주신 점은 좋은 시도라고 생각합니다!
다만 제가 말씀드리는 방식이 무조건적인 정답은 아닙니다. 정훈님께서 강의를 참고해서 해당 방식으로 작성해주셨다면, 단순히 예제를 따라 적용하기보다는 왜 이 방식을 사용하는지, 그리고 현재 코드에서 어떤 역할을 하는지를 이해한 뒤 적용하는 것이 더 좋을 것 같습니다.
자료를 참고해서 코드를 작성하는 것은 매우 좋은 자세라고 생각합니다. 다만 다음에는 참고한 내용을 그대로 적용하기보다는, 해당 방식이 필요한 이유와 적용 위치를 충분히 이해한 뒤 사용하시면 더 완성도 있는 코드가 될 것 같습니다!
이번 기회에 해당 부분이 어떤 역할을 하는지 한 번 더 정리해보시면 좋을 것 같습니다.
| import org.springframework.stereotype.Repository; | ||
|
|
||
| @Repository | ||
| public interface PostRepository extends JpaRepository<Post, Long> { |
There was a problem hiding this comment.
수정 완료해주시면 확인할 수 있도록 연락 부탁드립니다!
이번 코드리뷰가 단순히 코드를 수정하는 과정에서 끝나는 것이 아니라, 서로 잘 몰랐던 부분을 이해하고 실제 코드에 적용해보면서 한 단계씩 성장할 수 있는 시간이 되었으면 좋겠습니다.
정훈님도 이번 피드백을 너무 부담스럽게 받아들이기보다는, 더 나은 방향을 함께 고민해보는 과정으로 생각해주시면 감사하겠습니다!
과제명
REST API 서버 구현
💡 작업 내용
🔗 참고 링크
🤔 느낀 점 / 어려웠던 점