안녕하세요, Brad입니다. 오늘도 공부한 내용 정리해볼게요.
Q & A
DB없으면 Service Layer가 필요없을까요?
- 외부 서비스의 API를 이용해서 데이터를 가져오더라도 Repository(DAO)를 사용할 수 있습니다. 왜냐하면 그것도 데이터를 가져오는 일이기 때문입니다.
- 또 외부 서비스를 합쳐서 Repository를 또 만드는 일이 생기기 때문에 기본적으로 Service Layer가 필요할 것입니다.
- API 자체도 DB로 생각할 수 있습니다.
ResponsEntity
로 return하는 것과 그냥 User를 return하는 것이 차이는 뭘까요? - 클라이언트 입장에서 똑같습니다.
- 다만
ResponseEntity
는 임의적으로 담고싶은 데이터가 있을 때 사용합니다.
BasicAuthInterceptor의 용도는 뭘까요?
테스트할 때 로그인 처리를 하기 위해(세션값 부여) 사용합니다.
실제 배포할 때는 profile에서 설정을 통해 비활성화로 만들 수 있습니다.
- local - develop - production 구조로 배포합니다.
- develop은 production 서버랑 비슷한 구조로 되어있으며 여기에서 테스트가 진행됩니다.
- logback의 경우도 debug대신 info로 맞춰주는 등 배포로 가기 위한 과정이 있습니다.
설정은 수동보다는 자동화로 해서 실수를 방지할 수 있도록 하여야 합니다.
GUEST_USER의 용도는 무엇인가요?
로그인하지 않으면 null로 가져다니게 되는데 이 경우 NullPointException을 발생하기 쉽습니다. 이를 방지하기 위함입니다.
이를 Null Object라고 부릅니다.
그래서 null비교대신 Object로 로그인 유저인지 구별할 수 있는 것이죠.
loginUser == null
대신loginUser.isGuestUser()
로 비교할 수 있게 되는 것입니다.private List<Integer> getValues() { // return null; return new ArrayList<>(); }
Null Object 대신 Optional을 쓰는 것도 좋은 대안 중 하나입니다. 다만 Null Object의 장점은 그 안에 좀 더 로직을 넣을 수 있다는 점입니다.
1급 콜렉션의 의미는 무엇일까요?
- 새로운 객체인데 인스턴스 변수로 콜렉션이 하나 밖에 없는 것을 말합니다.
- Primitive(예 int)타입을 Wrapping(예 Integer)한 것과 같은 원리입니다.
- Annotation은 1급 Collection 클래스에서는
@Embeddable
, Entity 클래스 내에서는@Embedded
을 사용합니다. - 이렇게 함으로써 Entity클래스를 좀 더 단순화하여 유지보수에 좋을 수 있습니다.
JPA에서 FetchType의 LAZY와 EAGER의 차이는 뭘까요?(공부해야할 내용!!)
생각해볼만한 질문들(pobi의 질문)
로직은 Service와 Domain 중 어디에 구현되어야 할까요?
- OOP의 관점에서 Domain에 로직을 구현되는 것이 안전하고 유지보수측면에서도 좋습니다.
- 그리고 단위테스트도 하기 쉬운데 그 이유는 외부 API나 DB에 의존관계를 가지지 않기 때문입니다.
Service는 어떤 단위로 추가하는 것이 좋을까?
- 단순하게 생각하면 Entity 단위별로 하나씩 만드는 것으로 생각할 수 있습니다.
- 근데 저희 qna-atdd같은 경우
Question
과Answer
가 QnaService에 모두 담겨있었죠. 그 이유는Question
내에Answer
가 있고 QuestionRepository와 AnswerRepository가 동시에 필요한 경우가 있기 때문입니다. 이런 경우엔 하나의 Service내에서 처리하는 것이 좋다고 생각합니다.
필드 각 값에 대한 테스트는 어떻게 하는 것이 좋을까?
- 필드의 각 값이 Entity안에 모두 있는 경우
Equals
구현을 통해 일일이 필드값 확인을 할 필요없이 쉽게 테스트가 가능합니다. - 만약 필드의 값이 하나의 Entity값이 아니라 여러 Entity값들이 합쳐져 있다면 별도로 DTO를 만들고 이 안에서
Equals
정의를 할 수도 있네요!
- 필드의 각 값이 Entity안에 모두 있는 경우
메소드 인자는 어떻게 전달하는 것이 좋을까?
- 예를들어 질문을 수정한다고 했을 때 수정된
Question
객체를 전달할 수 있습니다. 메서드 인자로 객체의 값을 getter로 꺼내서 보내는 것이 좋은 방법일까요? - 그렇지 않다고 생각합니다. 왜냐하면 '상태값 가진 객체의 경우 그 값의 변경시 그 객체 안에서 변경하는 것이 좋기 때문'입니다.
- 그 이유에 대해선 여러가지가 있겠지만 제가 생각하는 바로는 getter를 쓴다는 말은 그 안의 데이터를 밖으로 꺼내서 그 밖에 로직을 처리한다는 말이고 혹시 로직의 변경사항이 생기면 해당 로직을 처리해야하는 부분을 모두 바꿔야한다는 말입니다. 결국은 중복코드가 발생할 확률이 높은 것이죠.
- 상태값을 가진 객체 내에서 로직을 처리한다면 해당 로직을 재활용할 수 있고 변경 발생시에도 그곳만 바꾸면 되기 때문에 유지보수에도 좋습니다.
- 예를들어 질문을 수정한다고 했을 때 수정된
Fixture(테스트를 위한 데이터)는 어떻게 생성하는 것이 좋을까?
Fixture을 만들기 위해 Builder Pattern을 적용해볼 수 있습니다.
Builder Pattern이 뭘까요?
package spring.builderpattern; public class Computer { //required parameters private String HDD; private String RAM; //optional parameters private boolean isGraphicsCardEnabled; private boolean isBluetoothEnabled; public Computer() { } public String getHDD() { return HDD; } public String getRAM() { return RAM; } public boolean isGraphicsCardEnabled() { return isGraphicsCardEnabled; } public boolean isBluetoothEnabled() { return isBluetoothEnabled; } //Builder Class public static class ComputerBuilder{ // required parameters private String HDD; private String RAM; // optional parameters private boolean isGraphicsCardEnabled; private boolean isBluetoothEnabled; public ComputerBuilder(String hdd, String ram){ this.HDD=hdd; this.RAM=ram; } public ComputerBuilder setGraphicsCardEnabled(boolean isGraphicsCardEnabled) { this.isGraphicsCardEnabled = isGraphicsCardEnabled; return this; } public ComputerBuilder setBluetoothEnabled(boolean isBluetoothEnabled) { this.isBluetoothEnabled = isBluetoothEnabled; return this; } public Computer build(){ Computer computer = new Computer(); computer.HDD = this.HDD; computer.RAM = this.RAM; computer.isBluetoothEnabled = this.isBluetoothEnabled; computer.isGraphicsCardEnabled = this.isGraphicsCardEnabled; return computer; } } }
출처 : https://www.journaldev.com/1425/builder-design-pattern-in-java
Builder클래스는 static class 로 만들고 필수 속성값은 생성자로 받고, Optional한 값들은 setter 메서드를 체인방식을 통해 채우는 것을 볼 수 있습니다. 그리고
build()
에서는 Builder클래스의 객체를 실제 만드려는 클래스의 값으로 build하는 작업을 통해 저희가 원하는 테스트 데이터를 얻을 수 있습니다.이렇게 만들면 장점은 테스트 데이터를 좀 더 쉽고 빠르게 만들 수 있다는 점입니다.
Builder 클래스는 보통 Entity안에 정의할까요? 만약 그렇다면 배포시엔 이 코드들을 어떻게 처리해야 할까요?
AcceptanceTest에서 Service, Repository 접근을 어느 정도까지 허용하는 것이 좋은가?
어떤 것을 의미하는 걸까요?
Optional의 사용
- 만약
Question
이라는 객체가 삭제되었거나 없는 경우 null값이 나오는 상황이 있을 수 있습니다. 물론 실제Question
객체가 존재할 수도 있죠. - 이 경우 고려할 수 있는 것이
Optional<Question>
입니다. 만약 찾는 질문이 없었다면Optional.empty()
로 반환할 수 있겠죠. - 받는 쪽에서는
Optional
타입이라는 점을 알고 null값이 반환될 수도 있다는 점을 염두해둘 수 있으며 그에 따라 Optional 객체를 다음과 같이 처리할 수 있습니다.question.orElseThrow(QuestionNotFoundException::new).update(loginUser, updatedQuestion);
- 만약
일급 콜렉션을 사용해 관련 로직을 분리
일급 콜렉션 분리를 왜 하는 것일까요?
메소드 분리는 어느 수준까지...
- 예를들어 질문 수정을 할 때 본인인지 확인하는 로직을 update 코드를 안에 넣을 수 있고, 또는 이 로직을 하나의 메서드로 또 뺄 수도 있습니다. 어느 정도까지 분리하는 것이 좋을까요?
- 전 로직의 복잡도에 따라, 또 재활용 여부에 따라 메소드 분리 결정할 것 같습니다. 로직의 복잡도가 크다면 그만큼 코드가 복잡해질 것이고 가독성을 더 높이기 위함입니다. 만약 재활용을 많이 한다면 더더욱 메소드 분리에 대한 필요성이 더 커질 것 같네요. 왜냐하면 코드의 중복은 위에서도 말했듯이 변경발생시 버그를 발생시킬 위험을 높이기 때문입니다.
Error Handling을 위한 Status Code
- 에러를 반환할 상태값도 고려가 필요하네요. 주어진 몇 개의 것만 사용하다보니 그런 생각을 못했던 것 같아요.
- 해당 서비스에서 어떤 상황일 때 어떤 Status Code를 사용할 건지에 대해 정의하고 그에 한해서 예측가능하게 Status Code 를 사용하는 것입니다.
'TIL' 카테고리의 다른 글
Todays' Dev Notes(2018-12-29) (0) | 2018.12.30 |
---|---|
Today's Dev Notes(2018-12-27) (0) | 2018.12.27 |
Today's Dev Notes(2018-12-20) (0) | 2018.12.20 |
Today's Dev Notes(2018-12-19) (0) | 2018.12.19 |
Today's Dev Notes(2018-12-17) (0) | 2018.12.17 |