본문 바로가기

회고

타팀 코드리뷰 분석

다른 팀 팀원들의 멘토님은 어떤 코드리뷰를 해주셨을지 궁금하고 많이 도움될 것 같아서 다른 팀의 코드리뷰를 보고 공통적인 것들을 정리해보았다.

 

1. 자원을 생성했을 때는 201(Created) 상태코드를 사용하는 것이 좋다.

더불어, 201(CreateD) 헤더의 요구사항인 Location 헤더를 충족하는 것도 좋다.

→ Location : 응답 메시지 본문에 새로 만들어진 리소스 혹은 리소스에 대한 링크를 메시지 본문에 넣어 반환한다.

 

ResponseEntity.created(URI) 
		.body(response)
		.build();

Spring의 ResponseEntity.create(URI)에 인자로 왜 URI를 받는지 알았다. (헤더에 Location을 알아서 넣어줌) 뿐만아니라 빌더패턴을 적용해서 바디를 넣을 수도 있었다.. 다음엔 더 꼼꼼히 살펴봐야겠다.

 

2. 엔티티들에게 공통적으로 관리되는 @MappedSuperClass 클래스의 경우 객체를 직접 만들어서 사용할 일이 없으므로 추상클래스로 만든다.

@MappedSuperClass
@EntityListeners
public abstract class BaseEntity {...}

 

3. 엔티티 기본 생성자의 접근제한자를 protected로 만들어라

전부터 궁금하던 내용이었는데 이참에 이유가 뭔지 찾아보았다. 우선 public 접근제한자를 지양하는 이유는 엔티티의 생성을 외부에서 차단하는게 더 안전하기 때문이다.

@Setter
@Entity
pubilc class Person {
	// id 생략
	private String name;
	private int age;
	
	public Person() {};
	public Person(String name, int age) {...}
}

위와 같은 엔티티가 있을 경우. 만약 name과 age의 값이 필수라고 하자. 이때 기본생성자가 위와같이 public인 경우 다음과 같은 불상사가 생길 수 있다.

Person person = new Person();
person.setName("Kong"); // age 값이 입력되지 않음

 

이때, Person의 접근제한자를 protected로 막아둘 경우 기본생성자를 사용하지 못하게 함으로써 위와 같은 문제를 막을 수 있다.

그렇다면 private이 아닌 왜 protected인가 ?

결론은, JPA의 프록시 때문이다. JPA에서 연관된 엔티티를 지연로딩으로 설정했을 경우 엔티티 조회시 연관된 엔티티를 프록시 객체로 가져온다. 이때, 이 프록시 객체는 실제 엔티티 객체를 상속한 타입이다. 즉, private 접근제한자를 사용하여 기본 생성자를 만든다면 프록시 객체에서 super을 호출할 수 없다. 따라서 Hibernate에서는 엔티티 클래스를 생성할 때 기본생성자로 public, protected 접근제한자를 필수로 사용하도록 권고하고있다. 더불어 엔티티 클래스를 final 클래스로 만들 수도 없다.(프록시 객체가 상속하지 못하게 되므로)

 

4. 검증시 Assert를 사용해보기

평소 입력값을 검증할 때 사용자 입력을 받을 때 DTO에서 한 번, 그리고 엔티티에서 한 번 해주었는데, 최근 세션에서 서비스 레이어에서도 입력값 검증을 해주는게 더 안전하다고 하셨다. 서비스 레이어에서 검증을 어떤식으로 해야할지 고민했는데 org.springframework.util.Assert를 사용하면 좋을 것 같다.

 

5. REST API에서는 자원을 복수로 표현한다.

@RequestMApping("/members")

 

6. Service에서 다른 Service 의존 vs Repository 의존

일반적으로 Service를 의존하는 것을 선호한다. 새로운 서비스 레이어에 비즈니스 규칙이 모두 적용되지 않을 수 있기 때문이다 (의존하려는 서비스 로직에 있는 검증로직 등을 빼먹지 않고 재사용할 수 있음). 하지만 의존하려는 서비스 정책과 다르게 적용되어야 하는 경우는 다른 서비스를 만들거나 Repository를 의존할 수 있다.

 

7. Service가 Controller에 의존하는 경우

Controller가 사용자로부터 값을 받을 때 사용하는 DTO를 그대로 Service에서 사용하는 경우 Service가 Controller에게 종속적이게 된다. → Service는 자신이 원하는 포맷으로 데이터를 받을 수 있어야 한다.

이 문제를 해결하기 위해 Controller에서 받은 DTO를 Service가 원하는 형태의 DTO로 변경해 주는 방법을 사용할 수 있다.

@GetMapping("/member")
public ResponseEntity<String> create(@RequesgBody MemberRequestDto requestDto) {
	MemberResponseDto responseDto = memberService.create(requestDto.toServiceDto());

	return ResponseEntity.created(...);
}

 

 

📜 References

잊을만 하면 돌아오는 정산 신병들 | 우아한형제들 기술블로그

Dto 사용시기에 대한 질문 - 인프런 | 질문 & 답변