올해 3월, 직장 생활을 시작했다. 블로그에 포스팅이 뜸해진 이유도 이것 때문이었다. iOS 개발자는 아니지만, 더 이상 공백기를 이어가기엔 부담이 커지고 있었고, 운 좋게 괜찮은 조건의 기회가 주어져 소개를 통해 입사하게 되었다. 아이러니하게도, 그동안의 취업 활동 중 가장 좋은 처우를 제안받았다.
그렇게 임베디드 리눅스 개발 포지션으로 커리어를 시작했다.
임베디드 개발자로서 9개월간 근무하면서 느낀 점은, 능력 대비 처우가 나쁘지 않다는 것이다. 다만, 제조업 기반의 환경은 IT 시장에 비해 최신 기술 문화에 대한 도입 속도가 느리고, 다소 보수적이라는 점도 체감했다.
입사 후 첫 업무는 팀에서 방치된 워크스테이션에 Git과 GitLab을 설치하는 작업이었다. 기존 리눅스를 재설치하고, SSH 데몬과 Git을 설정하는 등의 개발 환경 작업을 맡았다. 신입에게 이런 업무를 맡기는 것이 의아했지만, 회사가 그동안 외주 개발 방식으로 프로젝트를 진행해 왔다는 사실을 알고 나니 이해가 되었다.
그 후 외주 개발된 작업물을 전부 Git Mirror Clone을 통해 가져왔고, 2025년부터는 이를 기반으로 자체 개발을 진행할 계획이라고 한다.
입사 시점이 좋았다고 느꼈던 이유 중 하나는, 혼자서 Git을 다루는 방법을 익히기에 최적의 환경이었다는 점이다. 팀에서는 그동안 이슈 관리가 전혀 이루어지지 않았고, 이러한 불편함 해소를 요청받아 GitLab의 이슈 관리 기능에 대해 하나씩 알아가며 적용해 보았고 팀에 제안하였다.
요즘은 GitLab에 내장된 Wiki 페이지를 활용해, 9개월 동안 파악한 프로젝트 내용을 요약 정리하며 기술 문서를 작성하고 있다.
iOS 개발을 포기한 것은 아니다.
iOS 개발자로 커리어를 전환할 수 있을까라는 고민을 항상 하고 있다. 기술 스택이 다르다는 점이 걸림돌이 될 것 같아 걱정되지만, 현재 내린 결론은 간단하다. 신입의 관점에서 보면, 어디에서든 배울 것은 항상 존재한다는 것이다.
지금까지의 경험은 언젠가 분명 큰 자산이 될 것이라 믿는다.
2. 마라톤
취업 준비와 동시에 시작했던 운동이 달리기였다.
취업 후에 마라톤에 도전하겠다는 목표를 세우고, 동네에서 조용히 달리기를 이어갔다. 기간이 길긴 했지만 올해, 드디어 두 번의 10km 마라톤을 완주했다.
날이 추울 때만 뛰어서 꽤나 힘들었다. 2025년에는 따듯한 날씨에 참가해보고 싶다.
3. 운전
면허는 20살이 되자마자 취득했지만, 대학생 신분으로 차를 운전할 여건이 되지 않아 지갑 속에 고이 모셔두기만 했다. 그러다 회사가 대중교통으로는 출퇴근이 불편한 곳에 위치한 데다, 어머니께서 새 차를 구매하시면서 낡은 차 한 대가 남아 출퇴근용으로 운전을 시작하게 됐다.
집과 회사가 가까워 출퇴근 경로는 금세 익숙해졌지만, 아직도 초행길에서는 긴장감이 사라지지 않아 출퇴근 외에는 차를 거의 타지 않는다. 의외로 손세차가 재미있어서 요즘은 세차 관련 유튜브 영상에 푹 빠져 있다.
2025년에는..
다가오는 한 해에는 더 많은 것을 이루고, 지금의 나에게도 떳떳한 사람이 되고 싶다. 이 글을 읽는 분들 역시 계획한 일을 잘 이루고, 행복한 일들로 가득한 한 해를 보내시길 진심으로 바란다.
위치정보를 담당하는 locationManager 객체가 @Published로 정의되어 있다.
따라서 locationManager의 값이 변경이 되면 swiftUI는 자동으로 화면을 새로 그려낸다.
따라서 나는 ViewModel에서 locationManager의 속성 변경을 통해 UI를 갱신하도록 하였다.
locationManager 객체에는 사용자의 위치를 갱신하는 함수 locationManager(_, didUpdateLocations) 함수가 있다.
lastLocation 속성을 변경하는 동작이다. 해당 함수는 delegate 패턴으로 작성되며, 개발자는 해당 함수를 직접 호출할 수 없다.
그러면 어떻게 사용하느냐?
locationManager객체의 startUpdatingLocation() 함수를 통한 대리 호출만 가능하다.
해당 함수는 ViewModel에 정의하여, 여기서 호출이 일어나면 locationManager의 속성이 바뀌게 되고,
이를 SwiftUI가 자동으로 UI에 반영해 준다.
그리고 locationManager에는 사용자의 위치를 기반으로 주소값을 갱신하는 함수가 있다.
lastLocation을 기준으로 주소를 가져오게 된다.
이때 든 생각
어차피 따로 쓸 일 없는데, 호출 한 번으로 사용자 위치 갱신할 때 한 번에 주소도 갱신하면 안 됨?
내가 의도한 순서:
위치 갱신 -> 갱신한 위도, 경도를 기준으로 주소 갱신 -> 변경된 속성을 SwiftUI가 감지하여 UI 갱신
이런 단순한 생각으로 ViewModel에 다음과 같이 작성하였으나,
사용자 위치만 갱신될 뿐 정작 중요한 주소 레이블이 UI에 반영이 되지 않았다.
열심히 함수에 print()를 찍어 동작이 어떻게 이루어지다 파악해 보니 내가 작성한 것은 다음과 같이 동작했다.
즉 delegate 패턴으로 작성된 locationManager(_, didUpdateLocation) 함수로 넘어가기 전에 비동기로 주소갱신이 이루어진다.
당연하게도 갱신이 되지 않은 위치로 주소를 가져오니 이전 주소가 나오고,
위도, 경도 값이 경신된 이후 함수가 종료된다.
즉 locationManager의 대리호출과 비동기 호출의 조합으로 동작의 순서가 엉망이 돼버린 것.
문제 해결
짧지 않은 기간 동안의 삽질과 아직 해결되지 않은 의문점이 남아있지만 아무튼 지금은 의도된 동작대로 작동한다.
일단 위치갱신과 주소갱신을 한 번에 하겠다는 생각을 버렸다.
따라서 주소갱신함수는 locationManager의 속성을 바꾸는 것이 아닌, Sting을 반환하는 비동기 함수로 변경하였다.
* 비동기로 굳이 작성하는 이유는 주소를 반환하는 reverseDeocoderLocation() 함수가 비동기로 작성되었기 때문.
이어서 locationManager에 있던 주소 레이블 속성을 viewModel로 가져왔다.
이렇게 각 함수를 ViewModel에서 따로 호출할 수 있게 변경하였다.
이어서 View에서는 onChange를 통해 위도, 경도의 갱신이 완료되면, 주소를 가져오게 하여 동작의 순서를 정해주었다.
정리
동작을 분리한 이유는 다음 한 줄로 정리할 수 있다.
UI의 반영은 메인 스레드에서만 이루어진다.
ViewModel을 담당하는 FitcastManager가 @MainActor로 선언된 이유가 바로 여기서 작동하는 동작들은 모두 메인스레드에서 이루어지게 하기 위함이다.
따라서 기존의 방식대로 주소 갱신이 비동기로 동작하게 된다면 메인스레드가 아닌 새로 생성된 스레드에서 작동하게 되는 것이고, 여기서 이루어진 갱신은 UI에 반영될 수 없다고 판단하였다.
따라서 UI에 반영할 주소값의 갱신은 @MainActor로 선언된 ViewModel의 속성으로 두어 비동기로 작동하더라도 메인스레드에서 동작하게끔 수정해 주었다.
사실 해당 문제를 해결하기까지 아직 의문점이 남은 부분이 많이 있다. 아직 aync/await의 사용법과 MainActor와 관련된 이해도가 떨어져서 그렇다고 생각한다. 해당 개념에 대해 쉽게 설명한 글이 많으므로 나중에 완벽하게 이해된다면 다시 한번 리팩토링을 통해 후기로 돌아와야겠다.