-
Notifications
You must be signed in to change notification settings - Fork 70
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Team-11] 새로운 이슈 만들기 기능 추가, DIContainer 에 대한 고찰 #243
base: team-11
Are you sure you want to change the base?
Conversation
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.
유사한 패턴으로 문제가 발생하고 있습니다.
왜 불편한지에 대해서 느끼신다는 거 자체가 대단하신거 같네요.
이를 해결하는 방법은 필요한게 무엇인지 분리시키고 넣어줄 수 있는 부분들은 넣어주는 거 입니다.
뜬구름처럼 보이실 수 있지만, 전 다알려드렸습니다 ㅎ ㅡ ㅎ 화이팅이에요 ㅋㅋㅋ
var window: UIWindow? | ||
|
||
let container = Container() |
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.
앗..흔적이 남아있네요😅
Container를 써야 하는 곳은 많은데 객체는 하나만 만들고 싶어서, 싱글톤을 적용해보려 했다가
지난 PR에 static 객체에 접근하기보다는 다른 방법을 사용해보라고 하셨던게 생각나서 싱글톤 없이 해보려고 방향을 바꾸었습니다.
그러다가 (바로 아래와 같은) 더 안 좋은 상황이 발생한 것 같네요..🥲
@@ -64,7 +62,14 @@ final class IssueViewController: UIViewController { | |||
} | |||
|
|||
@objc func touchedAddButton() { | |||
self.navigationController?.pushViewController(Container().buildViewController(.newIssue), animated: true) | |||
guard let appdelegate = UIApplication.shared.delegate as? AppDelegate else { |
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.
음.. 앱델리게이트는 앱이 죽을때까지 살아있는데 그걸 오브젝트로 만들어서 위험하겠군요.ㅠㅠ
Container를 하나만 만들고, 만들어 둔 곳에서 참조해서 쓰려고 이런 식으로 접근했는데 다른 방법을 찾아보겠습니다.
return | ||
} | ||
self.navigationController?.pushViewController(viewController, animated: true) | ||
viewController.delegate = self |
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.
touchedAddButton()
말씀하시는 것 같아서 이 부분 글로 적어보겠습니다!
- 이슈목록(IssueViewController)에서
+
버튼을 누르면 해당 메서드가 실행된다 - 메서드 실행 시 AppDelegate의 Container에 접근해, 새 이슈 화면(NewIssueViewController)를 생성해 가져온 다음,
- 이슈목록(IssueViewController)의 navigationViewController에 새 이슈 화면(NewIssueViewController)를 push해 보여준다.
- 마지막으로 push로 보여지는 새 이슈 화면(NewIssueViewController)의 delegate를 IssueViewController로 설정한다.
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.
해당 부분에서 제가 보이는 시야는
AppDelegate에 포함된 객체를 접근하는게 아닌, 일련의 동작을 주입시켜주며 처리하는 방향이 보입니다.
그렇게 됐을때, IssueViewController는 AppDelegate, 화면 전환 로직을 모두 제거할 수 있게 됩니다.
guard let token = GithubUserDefaults.getToken(), | ||
let selectedRepo = selectedRepo else { | ||
return | ||
} | ||
guard let titleString = self.titleField.text, | ||
!titleString.isEmpty else { | ||
// TODO: - 타이틀 입력 값이 없다 => 얼럿 | ||
return | ||
} | ||
|
||
service.createIssue(title: titleString, repo: selectedRepo, accessToken: token) { boolResult in | ||
if boolResult { | ||
self.navigationController?.popViewController(animated: true) | ||
self.delegate?.created() | ||
} |
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.
'성격을 나눈다'는 게 어떤 의미로 해주신 말씀인가요? guard let token = ...
, guard let titleString = ...
과 같은 부분일까요?
일단 touchedCreateButton()
이부분도 글로 정리해 보겠습니다!
- 새 이슈 생성 화면에서 '저장'버튼을 클릭하면
- 앱에 저장된 AccessToken과, 현재 선택된 저장소를 가져온다
- 현재 입력된 이슈 제목 값을 가져온다 (제목이 비어있으면 return)
- 제목, AccessToken, 선택된 저장소 정보를 가지고 Service를 통해 API에 이슈 생성 요청을 보낸다.
- 이 결과가 true이면, navigationStack의 직전 화면으로 돌아가고,
- 연결된 delegate에 이슈가 생성되었음을 알린다.
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.
객체가 담당하는 역할에 대해서 분리하는 걸 뜻했습니다.
작게 작게 나누는거에 초점을 두다 보니, 해당 자원이 필요할때, 막상 이상하게 접근하는 경우가 많았습니다.
NewIssueViewController의 경우도, 위 행동을 추상화 시킨다면, 버튼이 눌렸을때, 행동을 실행시키며
해당 행동은 NewIssueViewController를 만들 때 넣어주기만 하면 됩니다.
private let tableViewCellIdentifier = "tableViewCellIdentifier" | ||
|
||
init(token: String, options: [Repository]) { | ||
super.init(nibName: nil, bundle: nil) | ||
self.token = token |
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.
제 생각에는 OptionSelectViewController에서 필요한건 [Repository] 인데 그럼 이것만 넣어주면 토큰이 필요없어지긴 할 것 같습니다!
그런데 그러면 OptionSelectViewController를 만들어주는 Container에서 API요청까지 해서 [Repository]를 넣어줘야 하는데, 컨테이너가 이 역할까지 해줘도 괜찮은 건가요?🤔
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.
ViewController는 즉 View의 경우 이벤트를 발생시키는 역할만 충실히 만족하며,
해당 부분 외의 작업은 다른 영역에서 담당하는게 유지보수 및 테스트하기 용이합니다.
위 작업을 진행하기 위해서 Container 즉 DIContainer에서 해당 작업을 처리하는게 어색하게 느껴지지 않습니다.
반대로 ViewController를 생성하는데 토큰이 필요한 점이 더 어색하게 느껴집니다.
} | ||
let selectedItem = options[indexPath.row] | ||
delegate?.selected(item: selectedItem) | ||
self.navigationController?.popViewController(animated: true) |
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.
코디네이터 즉 컨테이너를 둔 상태에서 뷰컨끼리의 이동은 전부 추상화 될 수 있는 부분입니다.
- OptionSelectViewController 를 재사용 하기 위함 - 어떤 옵션 타입인지에 따라 다른 데이터를 불러오게 하기 위함
var updated: (([Repository]) -> Void)? | ||
|
||
private var optionViewData: [Repository] { | ||
private var ReposList: [Repository] { |
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.
카멜케이스는 유지 하셨으면 좋겠습니다.
completion(boolResult) | ||
} | ||
} | ||
} | ||
|
||
struct NewIssueModelEnvironment { | ||
let createIssue: (String, Repository, String, Label?, Milestone?, Assignee?, @escaping (Bool) -> Void) -> Void |
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.
타입이 길어지기 시작하면 typealias를 사용하셔서 의미를 부여하면 좋을 거 같습니다.
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.
아니면 의미가 있는 모델로 묶는 방법도 있습니다.
}) | ||
) | ||
}) | ||
) | ||
let viewController = IssueViewController(model: model, repo: selectedRepo) // Issue -> Issues | ||
return viewController | ||
case .newIssue(let repo): | ||
let model = NewIssueModel(environment: .init(createIssue: environment.issueService.createIssue(title:repo:content:label:milestone:assignee:completion:))) |
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.
클로저 주입할때, 놓치기 쉬운 부분입니다.
해당 Environment를 생서할 때, 보이지 않게 self.environment를 주입한 시점입니다.
즉 self를 강하게 캡쳐시켰기 때문에 리테인 사이클이 발생합니다.
…필요한 NewIssueFormat 구조체 추가
기존 VC에서 coordinator를 직접 소유하던 부분을 삭제하고 AppCoordinator에서 childCoordinators를 관리할 때 register된 coordinator 사용
…cker into bibi/refactor # Conflicts: # IssueTracker/IssueTracker/AppDelegate.swift # IssueTracker/IssueTracker/Issue/IssueViewController.swift # IssueTracker/IssueTracker/NewIssue/NewIssueViewController.swift # IssueTracker/IssueTracker/Repos/ReposViewController.swift
- delegate 프로토콜을 weak var로 갖도록 변경 - 클로저 내에 선언된 self 를 weak self로 변경
- 새 이슈 생성 시, "이슈생성 요청" 이후 "이슈목록 조회 요청" 을 보내 생성된 이슈가 반영될때까지 시간 간격을 두고 재요청을 한다. 이 때 indicator를 나타낸다. - 반영이 되면 이전 화면인 이슈목록으로 돌아가 이슈목록을 조회하고, 화면을 갱신한다.
[Bibi/refactor] DIContainer 및 Coordinator 적용 및 이슈목록 갱신 문제 디버깅
🙋🏻♀️ 리뷰어님께 🙋🏻
안녕하세요, 강운! DIContainer 에서 여러가지 문제에 봉착해 고민하고 있습니다. Container 를 활용해서 새로운 기능에 쓰고 싶은데, 이 부분이 어려운것 같습니다. 문제점을 정리해 봤는데, 좋은 아이디어 있으시다면 힌트주시면 감사하겠습니다...🙏🏻
📚 작업 사항
🌤 고민과 해결
01. Container 에서의 토큰 관리
문제 배경
: Container 의 역할하고 싶은 방향
의문
🤔지금 문제
AccessToken
에 접근하고, VC 생성 시 토큰이 필요한 VC에만 넣어 주고 싶다.UserDefaults.standard.string(forKey: **self**.key)
의 리턴타입이 String?이라서 옵셔널 처리를 해주어야 한다02. 이슈 목록 불러오기
❓❓❓현재 문제🤔🤔🤔
이슈 생성은 되었는데, 새로 생성한 이슈가 안보임.
→ 조회가 안되나? 해서 이슈목록 화면 갱신 코드를 점검함 - 정상임.
→ Postman으로 해도 안되나? 했는데 안보임.
→ 우리의 이슈 조회 API가 **
List issues assigned to the authenticated user
인데 assigned를 지정하지 않아서 안보이나? 해서 assignees를 현재 로그인한 유저로 넣어줌. 깃헙홈페이지에서 확인했을 때 이슈도 정상 생성되고, 로그인한 유저가 assignee로 할당되어 있음에도 해당 이슈가 보이지 않음.→ 유저에 대해 할당된 유저이슈를 조회해야 하는 건가 해서 공식문서의 다른 API인 **
List user account issues assigned to the authenticated user
를 사용해 호출해 보았는데도 우리가 만든 이슈는 보이지 않음.→ 다만 **
List repository issues
API를 사용해, 저장소의 owner와 repository를 지정한 상태로 호출했을 때는 우리가 만든 이슈가 정상적으로 노출됨.→ 방금 마주한 문제라 해결 방향을 어떻게 잡을지는 아직 상의가 필요합니다😢