Skip to content

hwiwls/FourtyTwoCM

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

FTCM

42CM 개발 및 학습일지(Click.ᐟ)


목차



프로젝트 소개

42CM

사용자의 위치를 기반으로 소통하고, 주변 상점의 제품을 결제할 수 있는 소셜 네트워크 서비스입니다.

개발 기간

2024.04.13 ~ 2024.05.31(기능 개발)
2024.07.01 ~ 2024.07.28(리팩토링, 버그 수정 및 채팅 기능)

iOS 최소 버전

15.0


기능

📝 게시글 업로드 📸 스토리 형식 게시글 조회
🗺 지도 기반으로 게시글 조회 🗑 게시글 삭제
🤍 게시글 좋아요 🔍 댓글 조회
💬 댓글 작성 💳 PG사를 통한 실제 결제
💬 채팅 조회 💬 채팅 발송
💬 채팅 실시간 수신 🔍 채팅방 조회
👤 내 정보 조회 🗂 내가 작성한 게시글 조회
🗂 내가 좋아요한 게시글 조회 👥 유저 팔로우 / 언팔로우



팀 구성 및 역할

구성원: iOS Developer(1), Server Developer(1)

담당 역할(휘진): 기획, 디자인, iOS 개발



기술 스택

  • 언어: Swift
  • 프레임워크: UIKit
  • 아키텍쳐: MVVM
  • 반응형 프로그래밍: RxSwift
  • 결제 시스템: PayGate
  • 사용한 라이브러리:
    • Alamofire
    • KingFisher
    • SocketIO
    • Realm
    • SnapKit
    • Then
    • Toast

프레임워크, 아키텍쳐 및 기술 스택 선택과정 보러가기(Click.ᐟ)



주요 기능 실행 화면

로그인 회원가입 게시글 조회
42cm_signin 42CM Sign Up 42CM View Post
지도 기반 게시글 조회 상품 결제 채팅
42CM View Map 42CM Payment 42CM Chat
게시글 업로드 게시글 삭제 댓글 조회 및 작성
42CM Upload Post 42CM Delete Post 42CM Comment
팔로우 / 언팔로우 게시글 좋아요 마이페이지
42CM Follow 42CM Like Post 42CM My Page



주요 기술

디자인 패턴

  • ViewModel에 Input-Output 패턴을 적용하여 데이터의 흐름을 단방향으로 관리하였습니다.
  • Router Pattern을 사용하여 네트워크 요청을 구조화하고 관리했습니다. 각 API 엔드포인트를 열거형으로 정의하고, URL, HTTP method, Parameter 및 Query String 등을 일관되고 체계적으로 설정하였습니다.
  • Repository Pattern을 이용하여 Realm method를 관리해 코드 구조를 명확하게 유지하고 재사용성을 높였습니다.

비동기 프로그래밍 및 네트워크 처리

  • 네트워크 요청의 단일 응답을 비동기적으로 처리하기 위해 RxSwift의 Single Trait를 사용하였습니다.
  • 토큰 만료 시 APIInterceptor를 사용해 자동으로 새 토큰을 발급받아 저장하고 요청을 재시도하도록 하였습니다.
  • API 통신에서 발생할 수 있는 다양한 에러를 관리하기 위해 APIError 열거형을 정의하고, 이를 토스트 메시지로 제공하여 UX를 개선했습니다.

데이터 관리

  • 데이터 중복을 최소화하고 일관성을 유지하기 위해 정규화를 고려하여 Realm 모델링을 수행하였습니다.
  • Realm의 List를 사용하여 1:N 관계를 구현하여 객체 간의 연관성을 효과적으로 관리했습니다.

보안

  • 토큰과 같이 민감한 유저 정보를 안전하게 저장하기 위해 KeyChain을 사용하였습니다.

코드 최적화 및 안정성

  • final 키워드와 접근 제어자를 사용하여 컴파일 최적화를 이루고, 코드의 안전성과 성능을 향상 시켰습니다.
  • Instrument를 활용하여 메모리 누수 여부를 검토하고 최적화 작업을 수행했습니다.
  • 디버깅과 실행 중 상태 분석을 위해 LLDB를 활용하여 런타임에 변수 값을 확인하고 코드 실행을 제어했습니다.



학습



트러블슈팅

의도적으로 PageViewController를 빠르게 넘길시, UI 렌더링 지연 및 Memory Leak 발생

문제 상황

UIPageViewController를 사용하여 게시글 조회 기능을 구현하였습니다. 이 기능은 5개씩 커서 기반 페이지네이션 방식으로 동작합니다. 그러나 사용자가 페이지를 의도적으로 빠르게 넘길 경우 UI가 제대로 렌더링되지 않는 문제가 있었습니다. 또한, Instrument를 통해 확인한 결과, 해당 기능에서 메모리 누수가 발생하고 있어 앱의 성능 저하와 비정상 종료 가능성이 있었습니다.

Memory Leak


문제 원인

기존 로직에서는 현재 페이지의 마지막(5번째) 게시글에 도달했을 때만 다음 페이지 데이터를 요청하도록 구현하였습니다. 이로 인해 사용자가 빠르게 PageViewController를 넘길 경우, 데이터 로딩 지연이 발생했습니다. 이는 다음 페이지 데이터를 요청하고 받아오는 동안, 사용자가 이미 다음 페이지를 넘어가고 있어 로딩이 완료되기 전에 UI가 준비되지 않았기 때문에 발생했습니다.


해결 방법

현재 페이지의 마지막 게시글에 도달했을 때가 아니라, 3개의 단위로 데이터를 사전 로드하는 로직을 추가하였습니다. PageViewController의 인덱스가 3의 배수일 때마다 다음 페이지 데이터를 미리 로드하도록 처리하여, UI가 제대로 렌더링되지 않는 문제와 메모리 누수 문제를 해결할 수 있었습니다.

Preloading

Goodbye Memory Leak



채팅방 전환 시 소켓 이벤트 중복 호출 문제

문제 상황

채팅 앱에서 채팅방을 전환할 때마다 소켓 연결 이벤트인 connect에 대한 로그가 여러 번 출력되었고, 같은 이벤트가 중복으로 처리되는 문제가 발생했습니다. 이는 중복 데이터 처리, 성능 저하, 메모리 누수 등을 초래할 수 있었습니다.


문제 원인

소켓을 통한 연결을 설정할 때, 이전 채팅방에서 등록된 이벤트 핸들러들이 제거되지 않은 상태로 남아 있었습니다. 이에 따라, 새로운 채팅방으로 이동하거나 새로운 소켓을 설정할 때 기존의 이벤트 핸들러가 중복으로 호출되어 동일한 이벤트에 대해 여러 번 로그가 출력되고 이벤트가 처리되었습니다.


해결 방법

채팅방을 나갈 때 소켓 연결을 끊는 함수에 기존 핸들러를 제거하는 코드(socket.removeAllHandlers())를 추가했습니다. 이 코드는 채팅방이 사라질 때 호출되며, 이를 통해 이전 채팅방에서 등록된 이벤트 핸들러들이 새 채팅방에서 중복 호출되지 않도록 했습니다.

Leave Connection



일정 관리

Jira를 활용하여 프로젝트 일정 및 작업 관리를 효율적으로 수행했습니다. 특히, 개인 프로젝트에서 스케줄 관리와 작업 추적을 위해 Jira의 이슈 트래킹 기능을 적극적으로 활용했습니다. 이를 통해 개발 과정에서 발생할 수 있는 혼란을 최소화하고, 일정을 효율적으로 조율할 수 있었습니다.

42CM Jira



회고

항상 그렇듯 뿌듯함과 아쉬움이 공존하는 프로젝트였습니다. 이전에는 커스텀 Observable을 사용했었는데, 이번 프로젝트에선 RxSwift로 전환하여 비동기 작업의 가독성과 유지보수성을 향상시킬 수 있었습니다. 이외에도 Router 패턴 등을 도입하여 관련 코드를 모듈화하고, 반복되는 코드를 줄이는 데 성공해서 굉장히 뿌듯했습니다. UI적으로도 난이도가 높은 화면들이 있었는데, 결국엔 그런 부분들을 해결해나가면서 굉장히 뿌듯했습니다. 하지만 프로젝트를 진행하면서 역시 몇 가지 아쉬운 점들도 있었습니다.

아쉬운 점 1) 비즈니스 로직과 데이터 처리 로직의 혼재

ViewModel에서 비즈니스 로직과 데이터 처리를 처리하게 했더니, ViewModel 코드가 지나치게 복잡해지는 문제가 생겼습니다. 네트워크 요청을 보내고, 응답을 받아 처리하는 모든 과정이 ViewModel에 포함되어 코드가 복잡해지는 문제가 생겼습니다. 뿐만 아니라, 기능 수정 시 많은 부분을 수정해야 하는 상황이 자주 일어났습니다.

아쉬운 점 2) 네트워크 요청 테스트가 어려움 ViewModel이 직접 NetworkManager를 호출하는 방식으로 구현되어 있었고, 의존성 주입이 적용되지 않아 테스트 시 실제 네트워크 호출을 피하기 어려웠습니다. 모의 객체를 사용한 테스트를 시도했지만, 네트워크 요청과 관련된 로직을 모의 객체로 대체할 수 없어서 어려움을 겪었습니다.


생각해본 해결방안

문제들을 해결하기 위해 여러 방안을 고민하던 중, 클린 아키텍처의 필요성을 깨달았습니다. 실제로 적용하지는 않았지만, 다음과 같은 방법들이 해결책이 될 수 있겠다고 생각했습니다.

1)유즈케이스를 이용해 비즈니스 로직의 분리

ViewModel에서 비즈니스 로직을 분리하여 유즈케이스 클래스로 관리하면 좋겠다고 생각했습니다. 예를 들어, SignInUseCase라는 클래스를 만들어 사용자 인증과 관련된 로직을 처리하고, ViewModel은 오직 뷰 업데이트만 담당하게 하는 것입니다. 이렇게 하면 ViewModel의 책임이 줄어들고, 코드가 더 깔끔해질 것 같다고 생각했습니다.

2) 의존성 주입의 도입

의존성 주입을 도입해 ViewModel이 필요한 의존성을 외부에서 주입받도록 설계하면, 테스트 시 모의 객체를 쉽게 사용할 수 있습니다. 이를 통해 ViewModel이 특정 네트워크 구현에 강하게 결합되지 않도록 하여, 유닛 테스트에서 다양한 시나리오를 손쉽게 검증할 수 있게 됩니다.

이러한 개선 방안들을 차차 적용해보려 합니다. 이번 프로젝트에서 경험한 문제들을 바탕으로, 더 나은 아키텍처를 설계하고 유지보수하기 쉬운 코드를 작성할 수 있도록 노력해봐야겠다 생각했습니다.



유지보수 계획

  • 클린 아키텍처 적용
  • DI 적용
  • 42CM만의 디자인 스타일을 프레임워크로 만들어보기
  • Core Haptics 적용
  • 클러스터링 적용
  • SwiftUI+Combine으로 마이그레이션 해보기

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages