View

300x250

이번에 야곰 iOS 리팩토링 강의를 2기로 수강했다!
ㅤㅤ

사건의 발단

플러터로 작성한 캡스톤 프로젝트를 리팩토링하면서 여러가지 고민이 있었다. 나름 구조를 고민하면서 코드를 수정하고 있었는데, 실질적으로 적절한 방향으로 계층을 분리하고 책임을 옮기고 있었나? 의존성을 분리하고 구체적인 코드를 빼내면서 모델을 추상화하고 재사용이 가능하도록 코드를 작성하려 노력했는데, 내가 잘하고 있는게 맞나? 한 커밋 한 커밋 리팩토링을 진행은 하고는 있었지만, 확신이 없는 느낌이였다. 쇠똥구리가 열심히 똥을 빚고 굴려도 똥은 똥… 🥲
ㅤㅤ
반 년동안 플러터로 프로젝트를 진행하면서 스위프트가 너무너무 그리웠다. 오버라이딩도 안되는 언어로 함수들을 깔끔하게 정리하는 것도 힘들고, 조악하게 조립해야하는 코드 형태가 너무 찝찝한 느낌. 반 년 넘게 플러터로 프로젝트를 진행하면서 ‘스위프트가 유려하고 아름다운 코드’라는 생각은 항상 들었던 것 같다. 지금도 프로젝트를 계속 이어가고 있지만, 여전히 정리가 안된 언어와 프레임워크라는 생각이 든다. ㅋㅋㅋ
ㅤㅤ
그런데 왠걸? 어느날 iOS 리펙토링 강의가 광고로 나에게 올라왔다. 지금 고민하고 있는 부분들에 대한 궁금증도 어느정도 해결하고 스위프트도 다시 쓰면서 감을 되살릴 수 있겠다는 생각에 혹했다. 3주동안 진행하는 프로그램에 비해 가격이 학생인 나에게 조금 비싸긴 했지만, 그만큼 얻을게 많다면 괜찮겠다 싶어서 바로 결제를 했다.
ㅤㅤ

뭘 배웠냐면

ㅤㅤ
어떻게 보자면 소프트웨어 공학 전공수업에서 하지 못한 실습과 스위프트를 스위프트스럽게 코드를 작성하는 방법에 대해 배운 것 같다. 또 여러가지 최적화 기법과 테스트 코드 작성에 대해 배웠다. 한 번 쯤은 알아보고 싶었던 간질간질한 부분들을 잘 긁어주는 강의였던 것 같다.
ㅤㅤ

  • SOLID 원칙을 생각하며 코드 작성하기
  • 객체지향 체조원칙을 생각하며 코드 작성하기
  • 이 책임은 어떤 객체가 가지는게 적절할까? 코드가 훨씬 유연해질까?
  • 스위프트 문법에서 컴파일 타임을 최적화하기
  • 스위프트의 컴파일 방식에 대해 이해하며 런타임 실행 성능을 최적화하기
  • Xcode에서 테스트코드 작성하기 + TDD로 프로젝트 진행하기

ㅤ요런 것들을 배웠다. 일단 iOS를 다시 접한 것도 매우 반가웠고 스위프트 코드로 된 강의를 듣고 실습을 진행한 것도 좋았다. 또, 내가 진행한 실습 과제를 PR로 보내면 담당 리뷰어가 코드에 코멘트를 달아주는 것도 꽤나 마음에 들었다. 내 코드에서 작성한 부분들에 개선할 점을 달아주는게 메인이였지만, “이런 기능 확장이 필요한 상황이라면 어떻게 수정해야할까?”, “현재 코드에서 작업을 병렬로 실행하기 위해서는 어떻게 수정해야할까?” 등의 질문들도 남겨주셨다. 하나하나 껍데기를 벗겨보면 Concurrency나 Queuing같은 중요한 개념들이었고, 내가 잘 모르지만 분명 알아야하는 내용들이였다. 요런걸 나에게 인식시켜준게 만족스러웠다.

뭘 느꼈냐 하면

일단 내가 플러터 프로젝트를 클린 아키텍처로 리팩토링하면서 잘 해내고 있었던 부분과 못하고 있었던 부분들을 인식할 수 있었다. SOLID 라거나 객체지향 체조원칙 등은 사실 이때까지 전혀 신경쓰지 않고 개발하던 부분들이였는데, “내가 코드를 작성할 때 신경썼던 부분들에 이런 이름이 붙어있었구나. 나 코드 구조 잘 짜고 있었구나 😙” 라는 생각이 들었다 ㅋㅋㅋ. 저런 개념을 하나도 의식하지 않았는데도 자연스럽게 구조가 나오다니,,, 아마도 객체지향 코드가 지향하는 형태는 동일하기 때문이거나, 양질의 레퍼런스 코드 구조를 많이 봐서 그런가보다.
ㅤㅤ
플러터 리팩토링에서, 내가 작성한 클래스나 메서드의 이름이 매우매우 길어지는 일이 빈번했었다. 나는 그 이유가 오버라이딩이 불가능한 다트의 특성상 메서드명에 많은 정보를 담아야하기 때문이라고 생각했었는데, 더 큰 문제는 메서드가 너무 많은 책임을 가지고 있기 때문이라고 체조원칙에서 알려줬다. 이게 진짜 원인이 맞을 것 같음!! 메서드의 인덴트도 1단을 넘어가는 코드들은 별로 좋지않다고 했는데, 다시 보니 내가 작성한 코드의 함수들도 너무너무 길고 중복되는 코드들이 많았다.
ㅤㅤ
책임을 너무 많이 가지고 중복이 많아지게 된 이유는 요런 것들이라 생각된다.

  • freezed로 만든 모델과 엔티티
    • freezed로 모델의 생성부분을 처리해버리다 보니, 내가 적절히 커스터마이징 해줄 수 없었다.
    • @freezed class GroupEntity with _$GroupEntity { @JsonSerializable() factory GroupEntity({ required String groupName, @Default('') String? groupDescription, @Default('') String? groupRule, ... @JsonKey(includeFromJson: true, includeToJson: false) required String groupId, }) = _GroupEntity; factory GroupEntity.fromJson(Map<String, dynamic> json) => _$GroupEntityFromJson(json); }
    • 요렇게 코드가 쓰여있다보니, 사실 내가 이 코드가 어떻게 동작하는지도 잘 모르겠었고 이 모델이 init이 가능한 데이터들이 들어왔는지, 들어오지 않았다면 nil을 반환해버리는 코드도 어떻게 넣어줘야 할 지 잘 모르겠었다. 사실 지금도 모르겠다.
    • 리팩토링 강의를 들으면서 ‘모델의 생성 조건에 대한 책임은 모델에게 맡기기’를 접했는데 이게 너무나도 유용한 방식이라고 생각됐다. 위 방식을 개선해 앞으로는 아래 스위프트 코드처럼 책임을 모델에게 넣는 방식을 사용할 것 같다. 최고의 방법!ㅤㅤ
    • struct GroupEntity { init?(groupName: String?, groupDescription: String?, groupRule: String?) { guard let groupName = groupName, let groupDescription = groupDescription, let groupRule = groupRule else { return nil } guard let name, desc, rule에 대한 조건 검사 (regex 검사 등) else { return nil } return GroupEntity(...) } private let groupName: String private let groupDescription: String private let groupRule: String ... func 필요한 함수() { ... } }
  • Usecase로 분리된 레이어와 메서드 오버라이딩이 안되는 다트, 환장의 콜라보
    • 우선 제일 큰 문제는 설계가 부족했던 점 (설계를 할 시간도 부족했음)
    • 도메인 레이어와 프레젠테이션 레이어의 소통, 데이터 교환을 Usecase가 담당해주고 있었는데, 각 화면의 요구사항이 다르다보니, 여러 화면에서 공통적으로 사용할 함수이면서 각 화면에 적합한 함수를 뽑아내는게 어려웠다.
    • 또, 메서드의 오버라이딩이 안되다보니 같은 종류인데 여러 메서드가 필요한 경우 하나하나 다 작성해줘야 한다.ㅤㅤ
    • // 내가 하고싶은 것 // 하나의 함수 오버라이딩 func doSomething(from userId: String, content: String) { ... } func doSomething(from userId: String, content: String, from: Date) { ... } func doSomething(from userId: String, content: String, from: Date, to: Date) { ... } // 플러터에서 내가 해야했던 것 // 각각을 별개의 함수로 분리하기 func doSomething(from userId: String, content: String) { ... } func doSomethingUntilToday(from userId: String, content: String, from: Date) { ... } func doSomethingForPeriod(from userId: String, content: String, from: Date, to: Date) { ... }
  • 모든 함수의 반환형이 Future<Either<T, Error>> 인게 꽤나 불편함
    • 특히 Usecase 코드와 Data Layer 코드를 작성할 때 요런 경우가 많았는데, 너무너무 번거로웠다. 나는 단순히 반복되는 부분은 다른 함수를 적용해 재활용해주고 싶었는데, 정작 함수들이 Future<Either<T, Error>> 으로 둘러쌓여있다보니 코드를 실행해줄 때 마다 핸들링을 해줘야하고, 이런 부분들이 쌓여서 재활용이 어렵게 만들었던 것 같다.
    • 근데 지금 다시 생각해보니, 이것도 결국은 책임 문제인 듯! 메서드의 실행 결과에 대한 책임을 핸들링하는 코드가 메서드가 아닌 외부에서 가지고 있어야 하는 부분이 계속 문제를 만들었던 것 같다. 그렇다고 메서드 자체가 어떻게 문제상황들을 처리를 해야할 지는 고민을 해봐야겠지만, Error가 튀어나올 수 있는 Async 함수가 잔뜩 있는 상황은 분명히 다루기 어렵다고 느껴졌다.
      ㅤㅤ

스위프트에서 컴파일 시간과 런타임에 최적화를 하는 여러가지 기법들은 내가 앞으로 어떤 것들을 공부해야 하는지를 알려준 것 같다. 결국에는 OS나 컴구조같은 부분들도 같이 알아야하며, 프로토콜이 메모리를 어떻게 사용하고 있는지 등의 동작 원리에 대해 이해해야 최적화도 가능함을 느꼈다. WWDC 영상들의 소중함을 다시금 느꼈고,,, ㅋㅋㅋㅋㅋ 스위프트 언어에 대한 공부를 할 때 앞으로 어떤 곳에 방향성을 두고 공부를 지속해냐가야할 지에 대해 잡아준 느낌!
ㅤㅤ
ㅤ이외에도 Concurrency의 Task나 병렬로 실행했을 때 실행되는 쓰레드가 누구인지 등의 여러가지 질문을 통해 공부가 필요한 부분들을 쵹쵹 확인할 수 있었다.

만족하냐면

3주짜리 강의 치고 학생에게는 가혹한 가격이였지만 (할부 덕분에 들었다) 배운 것들은 꽤나 괜찮았던것 같다. 여러 실습 과제를 제공해주는 것과, 피어리뷰가 아닌 현업자가 내 코드를 확인해주는 것도 좋았다. 또 내가 잘하고 있는지와 어디가 부족한 지를 확인할 수 있다는 점도 만족스러웠다.
ㅤㅤ
3주라는 기간이 지식을 습득하기에는 짧은 시간일 수 있겠지만, 내가 부족한 지점이 어디인지 보고 앞으로의 계획을 잡는데에는 충분한 시간이라 생각된다.
ㅤㅤ
열심히 살자!!

320x100

'경험 기록' 카테고리의 다른 글

🥳 24년 1분기 회고  (0) 2024.03.27
4주 동안의 갓생 챌린지  (0) 2024.01.29
2023년 상반기 회고  (1) 2023.10.04
Share Link
reply
반응형
«   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