티스토리 뷰

IT

한달 간 피드백 받은 내용 정리

미닉길 2019. 4. 30. 18:28
반응형

1. 강제 언레핑 사용하지 마세요.

2. 커밋은 변경하는 최소 작업단위로 작게 나눠야합니다.

3. 특정한 함수 내부에서 exit() 처리하는 것은 좋지 않습니다.

4. 메서드 이름은 동사로 정하세요.

5. enum을 포함해서 타입을 만들때는 대문자로 시작하도록 만드는게 규칙입니다.

6. 서로 상관없는 값이니까 중첩해서 if 구문을 넣을 필요는 없습니다.

7. 변수 이름과 함수 이름이 동일한 것은 어색하네요.

8. inputFromUser()는 (Int, Int) 튜플을 리턴하는데, ladderMake(humanNumber, heightMax)는 매개변수를 따로따로 전달하는 이유가 궁금합니다. 튜플을 그대로 넘기는 건 왜 선택하지 않았나요?

9. 코드리뷰는 제가 일방적으로 고치라고 지적하는 방식이 아닙니다. 제 의견을 내면 저를 설득하면 됩니다.

10. 함수가 한 가지 책임만 갖도록 함수를 분리해보세요. 공통적으로 반복되는 부분은 일반화해보세요.

11. 함수이름은 동사로 시작해야 합니다.

12. 입력받는 함수와 숫자 검증하는 함수에서 공통부분을 일반화해보세요.

13. 검증 단계는 입력 단계와 처리 단계와 같은 추상화 수준이 아닐까요?

14. Number 라는 단어는 숫자를 의미합니다. 개수, 최대수를 의도한걸까요?
15. 사람숫자와 높이숫자가 보다 명확한 의도한 내용을 설명하는 단어는 없을까요?

16. 보통 업무를 할때도 입력/출력 문구는 개발팀에서 결정하지 않습니다. 유연하게 대처하도록 만드는게 개발자가 할 일입니다.

17. 여기 함수에 input 이라는 단어가 몇 번 나올까요? 입력을 처리하는 함수라고 해서 input을 자꾸만 사용할 필요는 없습니다.

18. optionalInput 도 옵셔널을 명시할 필요는 없고, stringInput도 그 변수 역할이 무엇인지 의도를 표현하세요.

19. InputableVariableName.numberOfPeopleToParticipate 일때는 왜 -1 처리하는건가요?

20. 예외적인 상황이더라도 return 값으로 표현하지마세요. 대신 다른 방법을 고려하세요.

21. 사소한 표현의 차이지만 numberOfParticipants  maximumLadderHeight 형태는 왜 다를까요? 후자처럼 numberOf- 가 없어도 된다면 앞에서도 뺄 수 있지 않을까요?

22. 지금처럼 하드코딩하지 않고 로직으로 해결해보면 좋겠습니다.

23. 내부에서만 호출하는 함수는 private 접근 설정을 해서 내부 함수로 만드세요.

24. 변수 이름에서 Bool 같은 타입 정보는 제외하세요.

25. 내부에서만 사용하는 메소드는 private func 으로 지정하세요.

26. struct 내부 변수들도 항상 private var 로 선언해서 외부에서 직접 접근하지 못하도록 보호하세요.

27. 이전 단계에서 추상화해서 만들었던 함수는 어디로 갔나요?
28. 단계를 넘어갈 수록 앞 단계에서 만들었던 것을 충분히 재사용할 수 있지 않나요?
29. 새로 작성하려고 미션을 진행하는게 아닙니다. 요구사항 변화에 따라서 코드의 변화를 최소화하려는 겁니다.

30. InputView 객체는 입력에 대한 반복까지 처리할 필요는 없습니다. 단지 입력만 받으면 됩니다. 입력을 받아서 결과를 전달하고 상위 모듈에서 다시 호출할지, 반복을 할지, 문구를 출력할지 결정하도록 하세요.

31. 입력 단계에서 에러인데 왜 에러 이름은 LadderGameErrors 인거죠?

32. 여기도 최소한 하나의 함수로 만드세요. 전역 변수와 코드만 놓지는 마세요.

33. 보통은 init() 함수는 객체 선언부 윗 부분에 작성하는 편입니다.

 

이정도 로직을 모두 사용해서 전체 흐름을 테스트 하는 것은 단위 테스트가 아니라 통합 테스트 혹은 인수 테스트라고 부릅니다.
단위 테스트는 객체 하나의 메소드 하나에 대해 특정 입력 값이 들어갈 때 기대하는 출력값을 세부적으로 다양한 케이스에 대해 테스트해야 합니다.
객체 초기화 메소드 init 을 호출해서 객체가 생성되는지, 실패하는지 부터 시작해서 객체 메소드별로 성공한 경우 뿐만 아니라 실패하는 여러 케이스를 테스트 함수로 분리해야 합니다.
더 작은 단위로 단위 테스트를 분리해보세요.

 

여기 있는 Assert 구문 하나하나가 테스트 함수 하나로 분리되어야 할 것 같습니다.
그리고 테스트 함수 이름도 비어있는 경우, 하나인 경우, 높이가 낮은 경우를 포함해서 구분하는거죠.

테스트 함수는 구현 내용을 보려는게 아니라, 테스트 함수를 자동으로 돌려서 해당 기능들이 잘 동작하는 여부를 기계적으로 확인하려는 데 목적이 있습니다.
성공/실패한 테스트 함수 이름만 보고 어떤 기능이 성공/실패 했는지 명확하게 알 수 있어야 합니다.

다음 스탭부터는 테스트 함수를 좀 더 세분화하는 연습을 꼭 해보세요.

 

좌표계산기

1. 주요 객체, 구조체, 타입을 선언하는 경우라면 별도 파일로 분리하세요.

2. 지금부터는 main() 함수 이외에 함수는 꼭 어딘가 타입에 소속되도록 포함시켜보세요.

3. InputView 는 입력값을 처리하는 역할에 집중하는게 좋습니다.
그 값들로 어떤 객체가 생성되는지 지금처럼 MyPoint()를 포함하면, 다른 타입이 생길때마다 InputView가 더 거대해지고 복잡해집니다.
InputView는 입력하는 값을 받고, 그 값으로 데이터 구조를 생성하는 것을 분리해보세요.

4. 여기서 처리하는 로직도 한 번에 한 가지 동작만 하는 함수들로 분리하고, 그 함수들을 조합(composition)하도록 구성해보세요.

5. 흐름 제어를 하는 걸 InputView 내부에 포함하는 건 저는 반대합니다.
이런 문구 출력이나 흐름 제어는 최상위 모듈에서만 하는게 맞다고 생각합니다. 어떻게 생각하세요?

6. 데이터를 보관하는 객체와 어떤 로직에 따라서 데이터/타입을 만들어 내는 객체를 혼용하지 마세요.
MyPoint 객체 역할은 무엇인가요? 값을 검증하는 건가요?
InputView 전체를 참조하는 건 입력을 받으려는 건가요? 그런 의도가 아니라면 분리하는 게 좋겠습니다.

7. 여기서 pointCoordinate 변수를 업데이트 하는거라면 위에서 pointCoordinate = MyPoint() 만든 객체는 필요없는거겠죠? 인스턴스 생성 없이 그냥 선언만 해도 되지 않을까요?

8. 안내 문구도 하드 코딩하지말고 코드로 표현해보세요.

9. 여기서 계산하는 건 MyLine 에서만 쓰는 기능인데 MyLine 구조체로 옮기면 어떨까요?

10. Any 타입은 구체적인 타입이 아닙니다. MyPoint 와 MyLine 을 공통적으로 일반화할 수 있는 타입을 도출해보세요. struct를 일반화할 때는 어떻게 해야할까요?

11. 팩토리 패턴에 대해 공부해보세요. figure 에 "exit"를 넣지말고 다른 방법을 적용하세요.

12. 공통부분을 추상화하기 위해서 프로토콜을 도입한 것은 좋은 시도입니다만,
이렇게 아무런 일반화 요소가 없는 것은 프로토콜 역할로 의미가 없습니다.
공통 부분을 도출해보세요!

13. 특히 초기화 메소드에 매개변수에 배열을 사용할 때는 주의해야할 점이 더 많습니다.
왜냐하면 배열은 요소의 개수에 대해서 가변적이기 때문입니다.
지금도 배열 값이 없는 경우, 1개인 경우, 2개인 경우에 따라서 동작이 달라집니다. 그만큼 안전하지 않고 위험한 초기화 메소드가 됩니다.
그래서 몇 개 이내인 경우는 명확하게 주는게 좋습니다. 오히려 매개변수가 길어지면 빌더 패턴을 구현하는게 좋습니다.

14. 생성을 하기 위해서 검증 과정을 거치는 데, 대부분 private func 으로 되어 있어서요.
이 부분만 단위 테스트에서 세부적으로 테스트할 수 있냐는 겁니다. 입력/출력으로 검증 과정의 모든 기능을 테스트할 수 있느냐는 질문입니다.

15. 매개변수로 Substring 을 쓰는 건 위험할 수 있습니다.
왜 그럴까요? String 과 Substring 차이를 학습했던 것 같은데 주의할 필요가 있습니다.

16. 여전히 속성을 public 으로 오픈하고 있네요. private 을 우선적으로 고려하고 read-only 가 필요한지 고민해보는 게 좋습니다.

17. 여기 있는 inputToPoint(), inputToLine(), inputToTriangle() 함수들 내부 동작은 세부적으로 테스트 가능한가요?
일부는 별도 객체로 분리해도 좋지 않을까요?
단위 테스트는 한 번만 하고 안하는게 아니라, 앞으로 모든 스탭 문제를 풀면서 항상 고민해야 하는 겁니다.

18. MyLine도 내부 속성이 private 속성이 아니었나요?

19. 의미없는 기능을 프로토콜 때문에 구현해야 하는 상황이 되면 해당 프로토콜을 잘 못 채택한 걸 수 있습니다.
혹은 그 프로토콜 추상화가 잘못되서 구현하지 않아도 되는걸 구현하고 있는걸 수도 있죠.

20. 매개변수로 배열을 사용하는 경우는 예외처리가 복잡해집니다.
왜냐하면 배열의 요소 개수가 다를 수 있기 때문이죠.
그래서 매개변수에 배열을 쓰는 것은 좋은 선택은 아닙니다. 가변적으로 넘어갈 내용이 n개 이상 많아진다는 가정이 있을때 선택하는 게 좋습니다.
아래 있는 point만 넘기는 걸로 충분하지 않나요?

21. 여기 출력하는 drawPoint(), drawLine(), drawTriangle() 메소드를 일반화해서 공통 부분을 만들 수 있지 않을까요?

22. 여기 if-else 구문을 제거하고 다형성을 활용할 수 있는지 검토해보세요.

23. FigureType 이란게 생겼군요!
이렇게 타입에 대한 정보를 만들 필요가 전혀 없습니다.
프로토콜을 활용한 다형성에 대해 학습해보세요.
switch-case 구문을 제거할 수 있을 겁니다.

24. draw() 라는 메소드를 일반화시킨 것은 좋습니다.
다만 여기처럼 객체가 출력을 위해서 직접 print()를 호출하면 안됩니다.
객체에 물어볼 수는 있지만, 출력할 형식을 완성하고, print() 호출해서 출력하는 것은 OuputView 역할입니다.

25. ANSI 코드를 포함해서 출력 문구를 형식화 하는건 객체 내부에 있으면 안됩니다.
iOS 앱 화면에 출력할지, 콘솔에 출력할지에 따라 형식은 달라져야 하는데 그걸 객체 내부에서 결정하면 안되는거죠.

26. ANSI 코드는 사라졌지만 문구와 값을 한꺼번에 출력 형식에 맞추고 있네요. 이 부분도 분리해서 OutputView에서 format 을 만들어서 출력하도록 개선하세요.

27. ment는 MyPoint와 전혀 상관이 없죠?
이런 경우는 프로토콜을 분리하고 꼭 필요한 객체에서만 채택하도록 분리하는 게 좋습니다.

 

아이고 여전히 단위테스트를 이렇게 한꺼번에 하고 있다니요... ㅜㅜ
CoordinateCalculatorTest 대신 RectTest 같은 클래스를 만들고
각 기능 요소들에 대한 입력 조건들 별로 단위 테스트 함수를 나눠서 진행해야 합니다.
그러다보면 Assert 문이 테스트 함수에 거의 하나씩만 매칭되게 될 겁니다. 참고하세요.

반응형

'IT' 카테고리의 다른 글

정규표현식 공부 방법  (0) 2021.06.16
슬라이드쉐어에서 한국어가 깨질 때 해결 방법  (0) 2020.12.07
SOLID 원칙  (0) 2019.05.16
도미닉의 용어 정리  (0) 2019.05.03
윈도우 서버 2008 R2 원격 복사 안될 때  (0) 2015.07.06
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함