아삭아삭 iOS 개발

[앱출시 _ Trady] Trady Project Post-Mortem 본문

1인 앱개발

[앱출시 _ Trady] Trady Project Post-Mortem

바닐라머스크 2022. 10. 6. 17:41

 

드디어 오늘 오전 11시 18분!!

가슴아픈 리젝 이후.. 재심사를 거쳐 Trady가 첫 출시되었습니다~! 🕺

 

지난 4주간의 기억이 희미해지기 전에 간략히 출시 회고를 해보려고 하는데요,

포스팅이 길어질 수 있으니 궁금하신 분들은 차근차근 끝까지 읽어주세용ㅎㅎ

 

처음 알게된 사항이나 사용해본 기능들이 있어서, 틀린 내용이 있을 수 있습니다.

피드백은 언제나 환영입니다~!

 

 

Data Points

  • 개발자 : 저요!👩🏼‍💻
  • 출시일자 : 2022.10.06
  • 개발언어 : Swift
  • 개발기간 : 2022.09.08 ~ 2022.10.06
  • 앱소개 :
    - 나만의 주식투자 매매일지 관리앱
    -
    기능의 목적은 미리 관심기업에 대해 공부하고, 계획하에 매매를 하는 습관을 갖도록 하는
  • 본 프로젝트 개인적 목표
    - 앱 출시를 경험해보자
    - 지금까지 SeSAC 수업, 과제 진행시 개인적으로 어렵게 느껴졌던 네트워크 통신, 데이터 구조, Realm을 다뤄보자
 

‎Trady

‎1. 관심기업 등록 - 국내 관심기업의 주요재무 현황과 배당 정보 3년치 데이터를 확인할 수 있습니다. - 거대한 주식 시장에 선뜻 투자하기 어려웠다면 관심기업의 희망 매매가와 매매일, 그리

apps.apple.com

 

Good Points & Bad Points

 

Good Point1) Realm List를 사용해서 데이터를 구조화시킴

피드백을 받기 전에는 '기업등록', '매매일지' 데이터를 별도의 realm 테이블로 구성할 예정이었다.

그러나 Trady의 목적은 매매하고자 하는 기업에 대한 데이터를 확인하고, 나만의 전략을 수립했다는 전제 하에 매매하는 습관을 갖는 것이다.

이 점을 고려한다면  '기업등록' 데이터가 먼저 등록된 후,

각 기업에 대한 일자별 매수&매도 내역이 쌓여가는 식으로 데이터를 구성해야 했기에 피드백을 받아서 구조를 수정했다.

 

 

위의 realm 파일 내역 사진을 보면, 기업등록 데이터인 'CorpRegisterRealmModel'안에 등록한 기업 정보들이 row로 쌓여있다.

그리고 각 기업별 매매일지 데이터들인 'TradingDiaryRealmModel' 내역이 'CorpRegisterRealmModel'의 tradingDiaries라는 변수 안에 마치 배열처럼 들어가게 된다.

이런 구조를 공식 문서에서는 To-Many Relationship이라고 정의하고 있다.

(source: https://www.mongodb.com/docs/realm/sdk/swift/model-data/define-model/relationships/)

 

 

Good Point2) 싱글톤 패턴으로 Realm을 사용하여 메모리 낭비를 방지하며, 여러 화면에서 Realm 데이터 사용함

처음 기획대로라면 realm에 저장된 매매일지 데이터는 home화면에서만 보여지고 다루어질 예정이었다.

그러나.. 매매일지를 쓰는 의미가 더 있으려면 해당 데이터 기반의 포트폴리오나 전체 내역을 보여주는 화면도 필요하다는 피드백을 받고

portfolio화면을 추가하기로 기획이 수정되었다!

그러다 보니 home화면 뿐만 아니라 자산현황, 매매내역 등 다른 여러 화면에서도 realm에 저장된 매매일지 데이터를 쉽게 꺼내서 핸들링할 수 있어야 했다.

 

 

그래서 Trady 실행시 TradingDiaryRepository 클래스를 최초에 한 번만 메모리에 할당해주고(Static),

이렇게 고정된 메모리 영역에 생성된 인스천스 객체 1개만 계속 사용하도록 했다.

실제로 여러 화면에서 데이터를 꺼내서 핸들링하고 편했고, 메모리 낭비도 방지할 수 있었다.

// ex1)
// home화면에서 calendar 특정날짜 클릭시 보여줄 데이터를 필터링할 경우, 미리 함수를 생성해두고 가져다 쓰거나
func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) {

    TradingDiaryRepository.standard.filteredByTradingDate(selectedDate: date)
    isEmptyCheck()
    self.mainView.tableView.reloadData()
}

// ex2)
// 매매내역 화면에서 기간, 매매구분 등 조회조건 결과를 필터링할 경우, 여기도 생성해둔 함수를 가져다 씀
func filteringQualificatinos() {
    TradingDiaryRepository.standard.filteredByAllTrading(from: mainView.fromDatePicker.date, to: mainView.toDatePicker.date, buySellIndex: mainView.segmentControl.selectedSegmentIndex)
}

 

 

Good Point3) 별도 생성한 다수의 Struct들을 DTO, VO로 구분하여 사용함

정신없이 작업을 하다가 문득 정신차려보니, 아래처럼 별도 생성한 구조체 파일이 너무너무 많아져 버렸다ㅠㅠ

 

왜 그런가 생각해보니

1) API통신도 여러번 하고,

2) 통신한 데이터 중 사용할 데이터만 추출해서 사용하는 경우도 있고, 

3) '통신해서 보여주는 데이터 +  사용자가 입력하는 데이터' 다 합해서 Realm Model로 통합 저장하기도 하는 등

이런저런 사용목적에 따라 만들다 보니 너무 많아져서 이게 맞는 방향인가 의심스러웠다.

 

이와 관련해서 내가 생성한 구조체를 다 사용하더라고 문제는 없고,

오히려 기능을 명확히 분리할 수 있어서 좋은 구조가 될 수도 있다는 피드백을 받았다!

 

그리고 DTO나 VO라는 키워드를 많이 쓰기도 한다고 하셔서
관련 내용을 찾아보고 나름 적용을..해보긴 했는데 적절하게 잘 활용한 것인지 확신은 없다ㅠㅠ 샘플 더 찾아보고 수정해나가야 할 듯 하다.

 

 

아래는 '관심기업 등록'화면에서 사용자가 기업을 검색했을 때 보여줄 정보를 가져온 방식을 정리해둔 그림이다.

기존에 쓰려고 했던 API를 토큰이슈가 있어서 제외하게 되었고, 어떻게든 보여줄 수 있는 정보들을 가져오려고 박박 긁어보다 보니

아래처럼 되었다.

개발하기 전에 '여기랑 저기서 이 키워드로 엮어서 통신해서 가져오면 되겠네~' 생각만 하고 문서로 정리를 안해뒀는데,
그래서 정리가 안되어서 고생을 많이 했다ㅠㅠ

다음부터 데이터 구조 정리를 기획 단계에서부터 꼼꼼히 해야겠다.

관심기업 등록 API 통신경로

 

 

Good Point4) Alamofire 통신시 status code별 분기처리 대응 

이전까지는 Alamofire로 네트워크 통신을 해서 데이터를 받아올 경우,

Alamofire내 통신 성공시 실행되는 부분에서 필요한 데이터를 다 처리한 그 결과만 뷰컨으로 보냈었다.

 

하지만 이번 프로젝트에서는 네트워크 통신 결과별로 대응을 해주고자 아래처럼 처리를 해주었다.

1) NetworkResult라는 열거형을 생성해서 네트워크 통신의 모든 케이스를 분류하고

2) APIManager에서는 통신의 결과와 코드를 합쳐서 일단 뷰컨으로 다 보냈다.

3) 뷰컨에서 성공적으로 받은 데이터를 처리하고, 그 외에는 알림으로 대응했다.

 

확실히 통신 실패나 에러 등 여러 케이스에 대한 대응을 해볼 수 있었지만

이전 방식에 비해서 뷰컨이 무거워진 점이 아쉬웠다.

 

더 구글링을 해보니 router를 만들어서 쓰는 방법도 있던데.. 솔직히 아직 이해가 가지 않아서 적용하지 못했다.. :(

 

 

 

Bad Point1) 무리한 공수산정 &  뒤바뀐 작업 진행순서

처음 공수산정시 (뇌를 비우고 했었는지 뭔지) '하루에 뷰1개, API 통신 1개' 처럼

너무 포괄적으로 간략히 산정해뒀다... 지금 말도 안되는 공수 산정이었다..

다음부터는 '화면별 > 해당화면내 기능별 > 해당 기능의 상세 제한요소 등' 처럼 depth를 세분화해서 정리해봐야겠다.

 

그리고 멘토님께서 작업할때 화면만 쫘아악 만들고 그다음에 기능 채우거나 통신을 붙이거나 하는 식으로 하지 말고!

한 화면에 한 기능이라도 제대로 만들고 넘어가라고 하셨는데..

그랬는데..

마음은 조급하고, 뭔가 눈에 보이는 (오늘의 삽질) 결과는 있어야겠고 하다보니 하지말란 식으로 점점 하게 되었다.

공수산정도 세분화하고 화면이나 기능 flow별로 차근차근 해나가자ㅠㅠ

 

Bad Point2) 초기 기획시 미비했던 데이터 구조 상세설계

아이러니하게도 위에 Good Point3) 과 연계된 부분인데, 초기 기획시에 데이터 구조와 흐름을 상세하게 문서화하지 않았다.

그래서 어느순간 정신 차려보니 아래처럼 작업 중에 급급하게 추가 기획하고, 급급하게 개발하고 있었다. 

 

 

그러다 보니 미처 생각하지 못한 예외사항이 있거나(많았음), 오류나 생기기도 하고(매우 많았음) 난리였다.

테스트할 때에도 이 부분에서 오류가 굉장히 많았어서 건들기가 무서웠고

빅 사이버 똥을 생성한 것만 같아 속상했다..엉엉엉

 

 

 마지막 날까지 차근차근 오류나는 사항들을 고치기는 했지만,

그 외에도 미리 조사하거나 설정해두었어야 하는 사항들을 상세하게 정해두지 않았기에 동일한 작업을 여러번 수정하기도 했다.

ex) 언제? 어떤 방식으로 통신? 예외처리 기술적 방식? 오류 케이스별 적절한 서비스적 대응통신할때 받는 데이터 타입?  데이터 핸들링시 타입? 데이터 저장할때 타입화면에 표기하는 방식? 어떻게 저장&업데이트 데이터 구분? Realm 테이블간 관계? 등

 

이번 프로젝트를 통해 시각적으로 문서화한 상세한 데이터 구조 기획의 중요성을 느꼈다.

앞으로 진짜 이러지 말아야지..

 

Bad Point3) 기능별 상세 예외처리 방안 기획 누락

데이터 구조 외에도 기능별 상세 로직이나 예외 처리할 사항들을 기획에서 누락했던 점이 아쉬웠다.

ex) 기업명 검색시 접근화면별 검색결과 표기로직

ex) 재무제표 데이터 표기시 다양한 금액 범위별 단위 읽기? 로직

ex) 매매일지 등록시 기업별 총 매도금액이 총 매수금액보다 클 수 없도록 제한 등

 

정리

  • 프로젝트 진행시 개인적인 목표였던 데이터 구조, API 통신 예외 처리, Realm을 다루어보는 목표 충족함
  • 앱 기획시 UI 뿐만 아니라 데이터 구조, 기능별 상세 로직에 대한 기획도 철저하게 하는 것이 중요함
  • 프로젝트 진행시 정해진 기한이 있을 경우, 특정 기능 구현시 (굳이 어려운 방법 파고들면서 연구하는데 시간쏟지 말고..) 일단 현재 내 수준에서 가능한 방법으로 해결하고 일단 넘어가는 것도 필요함
  • 각종 공식문서에서 제공하는 내용들은 꼼꼼하게 보자
    ex) Realm, 각종 API documents 등
  • 작업하는 기능별 오류 처리에 대해 기술적, 서비스적인 대응 방안을 항상 염두하고 개발하자

※ 더 공부해볼 사항

- Xcode로 파일경로 핸들링하는 다양한 메서드들 정리

- Realm의 다른 기능들 사용

- Realm 사용에 있어 thread와의 관계

- 비동기, Semaphore, async & await 활용방법

- String, Int, Double 핸들링하는 메서드들 총정리

- MVVM 적용방식

- SwiftSoup 사용

 


이번 포스팅 이후에는 새로 배웠던 개념이나 적용해본 내용들에 대해서도 별도 포스팅으로 차차 올려보려고 합니다.

 

더불어 Trady의 업데이트는 블로그와 함께 찾아올 예정이니

 

점점 더 발전하는 Trady의 기능과 화면들! 기대해주세용! 많관부~~~~