-
Notifications
You must be signed in to change notification settings - Fork 115
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
오픈 마켓 [STEP 1] 아리,제리 #82
Conversation
- Products 타입에 pages 타입 변경 - Parser내 encode 파라미터 추가 - Swiftlint exclude 추가
- 파싱 테스트를 위한 json 파일 추가 - Vendors, Image 타입 추가 - Product 타입을 리팩토링 - SwiftLint에 경고 제외할 문자열과 옵트인 룰 추가
- SwiftLint yml파일에 disabled_rules 추가
- NetworkError에 case 추가 - ParserError에 case, errorDescription 추가하고 네이밍 수정 - Parser에 Encoder, encode 메소드 추가 - 상품의 Secret을 조회할 때 encoding 해야하는 타입 Secret 생성
- API 기능 명세에 있는 Currency 타입 추가후 전반적으로 리팩토링 - 상품 수정에 필요한 타입 ProductModification 추가
- Data 타입 extension 추가 - 파일 그룹화 - httpbody 생성하는 createBody() 추가 - multipart form body 구성을 위한 multipartForm() 추가 - imageFile, productRegistration 타입 추가 - multipartform protocol 추가
- 줄바꿈 최소한으로 제거 - 가독성 향상을 위한 변수 대입 - private 메소드를 extension으로 분리 - 접근제어 추가
- NetworkError에서 statusCode의 연관값 타입을 수정 - Vendors의 들여쓰기를 수정
- 의존성 주입을 위해서 Sessionable 프로토콜 추가 - Network의 session 타입을 Sessionable로 변경하고 이니셜라이저 추가 - URLSession 테스트를 위해 Sessionable 프로토콜 채택 - 테스트를 위한 MockSession, MockURLSessionDataTask 타입 추가 - Network의 excute 메소드 성공과 실패 케이스 작성
- 의존성 주입을 위해 Networkable, Parsable 프로토콜 추가 - NetworkManager의 Network, parser 타입을 Networkable, Parsable로 변경 - 테스트를 위한 MockParser 추가 및 Parserable 프로토콜 채택 - NetworkManager의 fetch() 성공과 실패 케이스 코드 작성
- URLSessionDataTask는 init()을 정의하니 deprecated 경고가 떠서 삭제 후 로직 변경 - Sessionable 프로토콜을 활용하여 의존성 주입을 주었던 부분 제거, 관련된 Mock 객체 삭제 - 테스트 코드에서 DummyData, MockSession 대신에 새로 만든 Session을 활용하도록 수정 - Mock JSON 파일 활용을 위해 stubProduct 프로퍼티와 StubProduct 타입 추가
- setUpWithError, tearDownWithError 메소드를 재정의하여 테스트 기본 세팅 리팩토링
- JSON이 아니라 multipart/fome-data로 수정 - boundary 변수를 함수 내부에 추가하여 활용하도록 수정 - 테스트 파일 내부 그룹으로 분리
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@leeari95 @llghdud921
안녕하세요 아리 제리!!
이번 프로젝트에서 처음 뵙네요😁
2주간 잘 부탁드립니다🙇🏻
처음에 러닝커브도 있는 쉽지 않은 프로젝트인데 너무 상세하게 아주 잘 구현해주셔서 놀랐어요🤭
본의 아니게 재밌게(?) 리뷰하다보니 코멘트들이 다소.. 아니 매우 많아졌어요ㅠㅠ...
저도 캠퍼해봤던 입장이라 리뷰 코멘트가 많으면 좋긴하지만 반면 짜증도 났기에 누구보다 그 입장을 잘 알기에..
우선 죄송합니다😭
다만 많은 코멘트에서 거의 대다수는 수정 요청이 아닌 정말 궁금해서 의견을 묻는 요지들이 많습니다.
(코드와 아주 별개의 것도 있구요..🥲)
그럼 리뷰를 너그럽게 봐주시면 감사하겠습니다🙌
칭찬 드리고 싶은 부분
- SwiftLint 룰을 아주 잘 도입해보셨네요! 아주 유용할거에요 💯
- 파일 그룹핑이 아주 세세하게 잘 되어 있어 구조 파악에 편했습니다 💯
- 각 역할들을 최대한 잘게 쪼개 가독성이 좋았어요 💯
- 테스트 코드를 기가 막히게 디테일하게 짜셨군요 💯
- 리드미 작성 정말 나중에 보답 받으실거에요 💯
- 쉽지 않으셨을텐데 대체적으로 통신과 모델링 관련해서 잘 구현해주셨네요! 💯
고민하셨던 부분에 대한 저의 의견
- 단일 책임 원칙(Single responsibility principle)
- 이 부분이 아주 잘 되어 있으신거 보고 사실 좀 놀랐습니다🤭
정말 객체지향을 잘 알고 설계하신것 같았어요. 이 부분은 고민하신만큼 성과가 나왔던 부분인것 같네요!
- CodingKeys 활용
- 요 부분에 대해선 리뷰 코멘트로도 몇가지 남겨보았어요..!
스네이크 케이스의 변환만 카멜 케이스로 가져가고 싶다면 코딩키 전략을 사용해도 됩니다ㅎㅎ
또한 모델링에서 required 해야하는 부분인지도 남겨보았습니다!
(대다수의 자세한 코멘트는 이곳보다 코드 라인바이라인으로 남겨봤어요ㅎㅎ
혹시 이곳에 더 상세히 남기는게 더 편하시면 말씀해주세요!)
- NetworkManager와 Network
- 궁금한 부분이 있습니다!
왜 나눠주게 되었는지 기준이 궁금해요 사실🙋🏻
테스트의 목적을 생각하신걸까요?
아 저는 의견이 달라서 여쭤보는건 아닙니다!
실제 통신은 결국 네트워크 매니저를 통해 이뤄지는데Networkable
프로토콜과 그걸 따르는Network
타입을 만들어 거쳐 사용함으로
얻는 이점으로 어떤걸 생각하셨는지 궁금해요~!
- Name Space
- 정말 아주 최고에요👍
최대한 String Literal을 직접 코드에서 사용하는 지양하면 실수도 줄이고 개발자 에러를 줄일 수 있어서 저도 그렇게 사용합니다🙌
- Request, Response
- 아주아주 잘 모델링되어 있는 부분 확인했습니다!
다만 궁금한게 파일 그룹명이 모호한것 같아요.
사실 그룹안에 들어있는 타입들이 상품을 위한것 같아서 이를 아우룰수 있는 폴더 네이밍은 어떨까 생각이 들었어요🤔
Request / Response의 그룹 네이밍만 봐서는 하위 파일들을 보지 않고선 유추하기가 힘들것 같다고 느껴졌습니다.
그 하위 파일들을 각 모델로 나누고 구현하신 부분은 좋은것 같습니다!
- Overloading function
- 이 부분도 궁금한걸 리뷰 코멘트에 적긴했습니다🙋🏻
요약하자면 "개발자가 네이밍이 같아 파라미터로 구분지어줘야하는 구현에서 실수할 여지가 있지 않을까?" 였습니다🧐
- Test Doubles
- 테스트 저도 약한 부분인데 두분이 너무 잘 구현해주셨더라구요!
Mock과 Stub의 사용도 적절해보이구요.
아마 오동나무가 테스트 더블 관련해서 강의를 만들었던(?) 맞는지 모르겠지만 아니라면 준비하고 있을거라 생각되고..!
그걸 보셨는가 싶지만 이와 별개로 저는 test double에 대해 개념을 이 레퍼런스로 잡았던 기억이 있어 공유드립니다🙋🏻
https://blog.pragmatists.com/test-doubles-fakes-mocks-and-stubs-1a7491dfa3da?gi=e7d32c619169
저는 현업에서 fake 구현을 가장 많이 사용해요.
stub 테스트는 결국 원하는 도출값을 내놓고 시작하기에 실제 통신을 타는지 어떤지 확인할때 모호합니다.
이에 fake 구현은 가장 실제 통신 및 로직을 다 처리해보지만 속은 fake인 그런 기능을 해주기에 어떻게 보면 가장 테스트 다운 테스트라고 볼 수 있어요.
한번 참고해보시면 도움이 되실거에요..!🙋🏻
궁금 혹은 조언 요청에 대한 저의 의견
- 비동기 메서드를 사용하는 동기 메서드는 비동기 메서드 테스트로 진행해야할까요?
- 정말 죄송한데 제가 이 질문을 정확하게 이해를 못했어요ㅠ..
조금 더 구체적으로 예시 혹은 설명을 주시면 다시 답변을 드려보겠습니다!
백프로 이해가 되지 않은 상태에서 유추해서 답변 드리면 더 혼란만 드릴것 같아서요🥲
- URLProtocol을 활용하여 Test를 진행
- 지금 같이 구현하는 방법은 저도 사실 처음봤어요!
왜냐면 저의 경우는 통신 관련해서도 대개 라이브러리를 쓰기에 URLSession 애플 라이브러리로 통신을 구현하는것은 캠프 이후로 많이 없습니다.
(보통 Moya, Alamofire를 쓰기에)
그렇기에 저의 현업에서도 두 라이브러리로는 테스트도 조금 더 수월하게 진행할 수 있습니다.
다만 외부 라이브러리라도 내부 구현은 애플 라이브러리 기조를 벗어날 수 없기에 상동하다고 생각합니다.
그래서 두분이 트러블슈팅에 잘 나타내주신것을 토대로 저도 학습을 해봤는데 지금 구현에 문제는 없다고 판단됩니다!
다만 해당 프로토콜을 채택하면서 사용되지 않는 메서드들도 오버라이드 한 부분이 있다고 보여서 리뷰 남겨봤습니다..!
- Health Checker의 필요성을 모르겠습니다.
- 사실 구현하기 나름이겠지만 현재 두분이 구현하신 코드에선 필요하지 않습니다.
Health Checker는 보다 네트워크 상태 모니터링에 필요한 부분입니다.
지속적으로 서버에 신호를 보내 응답이 오는지에 대해 체크하는 부분을 구현하고 이를 통해 잘못된 응답이 온다면
잘못된 리퀘스트가 아니라면 사실 서버가 죽은거겠죠?
그럴경우 바로 즉각적인 대응을 해줄 필요가 있어 그럴 경우 보통 사용되는걸로 알고 있습니다.
다만 클라에서 저도 그렇고 이 헬스 체커를 사용해본 기억은 없네요!
(물론 이를 가지고 필요하다면 구현을 해줄 순 있겠지만요..!)
그렇기에 지금처럼 필요하신 부분만 API문서에서 가져가셔도 무방합니다🙌
- 테스트 시 Request의 바디도 체크를 해야할까요?
- 음 제 의견은 굳이 안해도 될것 같습니다!
테스트 코드는 정말 스타일의 차이이긴 하지만 굳이 저거까지 체크해야될까? 라는 두분과 동일한 생각이 있습니다.
어차피 request body를 포함해 정상적으로 request 되었다면 그에 대한 응답을 확인하면 되는 부분일것 같아요.
해결되지 않은 부분에 대한 저의 의견
- 두가지 스탠스를 취할 수 있을것 같습니다🙋🏻
저 Identifier가 변치 않는것이라면 전역적으로 상수처리할수 있을것 같고
그게 아니라면 말씀하신것처럼 파라미터로 받을것 같습니다!
그런데 제가 알기론 보통 고유값일테니 상수로 받는게 어떨까 합니다!
두분 너무 고생많으셨어요..!
다시 한번 리뷰 코멘트가 너무 심각하게 많은것 같아 죄송스럽네요..😅
의견이 다른 부분은 바로바로 말씀해주세요ㅎㅎ
아 두분의 구현에서 다음 스텝으로 넘어가는데 지장은 없어 보입니다.
그렇지만 코멘트가 있다보니 한번 확인해보시고 수정할 부분은 수정 후 리뷰 재요청을
그게 아니면 머지 요청을 해주세요~!🚀
|
||
import Foundation | ||
|
||
struct StubProduct: Decodable {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
얘는 필요한 코드인가요?🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 코드는 NetworkManager의 fetch()의 디코딩하는 부분을 테스트하기 위해 decodable은 타입이지만 올바르지 않은 가짜 객체를 넣어 실패 케이스을 의도적으로 구현하였습니다.
// test하기 위한 부분
let parsingResult = parser.decode(source: data, decodingType: decodingType)
// test code
func test_Fetch_Decode_failure() {
let response = HTTPURLResponse(url: url, statusCode: 200, httpVersion: nil, headerFields: nil)
...
let decodingtype = StubProduct.self
- MultiPartForm 프로토콜을MultiPartFormProtocol로 변경 - MultipartForm, contentType enum 구현 및 관련 코드 수정 - 오버로딩된 request 함수명 명확하게 개선 - 들여쓰기 컨벤션 개선
그린!! 와우... 이렇게 코멘트로 뚜둘겨 맞아본 것은 처음이라... 행복합니다. 😁 🍏 수정사항
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
변경 사항 확인했습니다!
한가지 부분에 코멘트를 추가해봤어요🥲
그 외 전부 이상없고 아주 잘 구현해주셨습니다!
스텝 1은 이정도면 충분할것 같아 어프로브 후 머지하겠습니다~
변경사항이 생기는 부분은 마이너하기에 스텝2를 하시면서 같이 보완해보면 될것 같습니다!
고생하셨습니다🙌
++ 아실 수 있지만 깃헙에서 리뷰 재요청을 할때 우측 상단에 Reviewer 탭에서 새로고침 버튼을 눌러주시면
저에게 깃헙으로 재요청 알림이 갑니다!
활용해보시면 편할거에요ㅎㅎ
p.s. 아 커밋 메시지 규칙에서 궁금한게 하나 있는데요..!
두분은 단순 네이밍만 변경되는 커밋에는 어떤 prefix를 달아주고 계신가요?
현재는 refactor를 달아주는것으로 보이는데 이유가 궁금합니다🙋🏻
var sutURL: URL! | ||
var sutData: Data! | ||
var sutRequest: URLRequest! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
force Unwrapping 타입말고 조금 더 안전하게 사용해볼 수 있을까요?
테스트 코드여서 실제 앱 환경에서 문제는 없겠지만 forece Unwrapping은 항상 앱 크래쉬의 주요 원인이 됩니다🥲
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
테스트 코드여서 상관없다고 생각해서 느낌표를 붙여주었는데, 그린 피드백대로 안전하게 사용해보도록 하겠습니다! 😊
안녕하세요. 그린 @GREENOVER
아리 @leeari95, 제리 @llghdud921 입니다!
이번 오픈마켓 프로젝트 리뷰어로 만나뵙게되어 영광입니다. 🙇🏻♀️
오픈마켓 STEP 1 너무 어려웠는데... 저희가 잘 구현했는지 모르겠네요. 🥲
저희가 아래 적은 내용 이외에 부족한 점이나 놓친 부분이 있다면 번거롭더라도 한번 더 짚어주시면 감사하겠습니다. 🙏🏻
고민했던 점
1. 단일 책임 원칙(Single responsibility principle)
2. CodingKeys 활용
실제 네트워크에서 내려오는 변수명이 스네이크 케이스를 사용하는 변수는
Codingkey
를 이용하여 parsing하는 key를 바꿔주었으며, 스네이크케이스를 사용하지 않는, 즉 타입의 변수명과 일치하면 rawValue를 명시할 필요가 없어 가독성을 위해 한 줄로 case를 합쳐주었습니다.3. NetworkManager와 Network
Network
: dataTask()를 통해 SessionDataTask를 서버로 전송해 직접 네트워킹하는 객체NetworkManager
: Network의 excute를 통해 data를 받아 decoding하는 fetch()를 가진 객체4. Name Space
5. Request, Response
6. Overloading function
7. Test Doubles
궁금했던 점 / 조언이 필요한 것
(문제에 대한 자세한 사항은 README에 Trouble Shooting부분에
1번문항
을 참고 부탁드립니다!)해결이 되지 않은 점
identifier
가 필요한데요. 아직 배정받은identifier
가 없어서 임의로 하드코딩 상태로 남겨두었어요. 나중에identifier
를 배정받게 되면 수정할 예정인데, 이 부분은 파라미터로 전달받는게 좋을까요?