<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>iOS 개발 공부</title>
    <link>https://junbok97.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Fri, 31 Jul 2026 17:34:58 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>물복딱복준복</managingEditor>
    <item>
      <title>2024년 3분기 회고</title>
      <link>https://junbok97.tistory.com/386</link>
      <description>&lt;h1 data-heading=&quot;벌써 7개월, 끝이 보이지 않는 취업 준비&quot;&gt;벌써 7개월, 끝이 보이지 않는 취업 준비&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;취업 준비를 계속하다 보니 어느새 벌써 7~8개월이라는 시간이 지나 있었다. 부스트캠프가 6개월이었는데 그보다 더 긴 시간을 취업 준비에 쓰고 있다는 것이 잘 믿기지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8기 캠퍼들과는 종종 만났는데 취업에 성공한 친구들은 극소수였고 다들 힘들어했다. 그만큼 취업 시장이 너무 좋지 않았다. 오랜만에 만나서 벌써 부스트캠프 9기가 시작한다는 이야기가 나오자 다들 풀스택 챕터로 다시 들어가야 하나 하는 우스갯소리를 하기도 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;취업 준비가 길어지다 보니 이제는 더 이상 뭘 해야 할지 모르겠는 상황이 왔다. 1분기에 리멤버 리드님이 다양한 것을 해보라고 조언해주셔서 시중에 나와 있는 앱들을 분석하고 카피해보거나 기술적으로 고민이 필요한 것들을 만들어보기도 했는데, 이런 것들마저 이제는 큰 의미가 없는 것처럼 느껴졌다.&lt;/p&gt;
&lt;h1 data-heading=&quot;SwiftUI 스터디를 시작하다&quot;&gt;SwiftUI 스터디를 시작하다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무언가 방향이 필요하다는 생각에 개발 동아리를 알아보고 있었는데 마침 캠퍼들끼리 SwiftUI를 함께 공부해보자는 이야기가 나와서 다 같이 스터디를 만들게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그동안 UIKit만 하다 보니 iOS에 대한 애정이 조금 식기도 했었고, 결국 언젠가는 SwiftUI로 넘어가야 하니 미리 공부해두면 좋겠다고 생각했다. 혼자였다면 쉽게 지쳤을 텐데 같은 상황의 캠퍼들과 함께 공부하니 다시 동기부여가 되었다.&lt;/p&gt;
&lt;h1 data-heading=&quot;드디어 찾아온 취업, 그런데 협력업체 소속 ??&quot;&gt;드디어 찾아온 취업, 그런데 협력업체 소속 ??&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러던 와중에 드디어 취업에 성공했다. 동원의 앱 개발자였는데 특이하게도 동원 소속이 아니라 협력업체 소속으로 동원에 출퇴근을 하는 형태였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;말로만 듣던 대기업 하청 구조를 직접 겪어보니 참 신기했다. 내 소속인 협력업체 직원이 면접 당일 동원 사람들을 소개해주는 것이 처음이자 마지막 면접이었고, 출퇴근도 동원으로 하고 동원 팀장님의 업무 지시를 받고 근태 관리도 전부 그곳에서 이루어졌다. 면접도 동원 팀장님이 보셨다.&lt;/p&gt;
&lt;h1 data-heading=&quot;그럼에도 입사를 결정한 이유&quot;&gt;그럼에도 입사를 결정한 이유&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 1분기였다면 이 회사에 입사하지 않았을 것이다. 내가 원하던 회사의 조건은 자사 서비스가 있으면서 네이티브 앱을 개발하고, 나보다 연차가 있는 iOS 개발자가 있는 팀이었다. 그동안 혼자 개발하거나 같이 공부하던 사람들끼리만 개발을 해왔다 보니 시니어 개발자에게 제대로 된 피드백을 받고 싶은 니즈가 컸기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 취업 준비 기간이 길어지면서 생각이 조금 달라졌다. 나는 어디서든 배울 것이 있다고 생각했고, 어떤 것들은 실제로 경험해봐야만 체득할 수 있다고 믿었다. 무엇보다 혼자서 iOS 개발을 온전히 책임지는 경험은 지금이 아니면 하기 어려울 것 같았고, 오히려 도전적으로 다양한 것들을 시도해볼 수 있겠다는 생각이 들었다. 그래서 나는 바로 입사를 결정하게 되었다.&lt;/p&gt;
&lt;h1 data-heading=&quot;세 개의 앱, 그리고 뒤죽박죽인 코드베이스&quot;&gt;세 개의 앱, 그리고 뒤죽박죽인 코드베이스&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 맡은 업무는 동원몰, 더반찬, 아르르라는 동원의 앱 3개를 개발하고 운영하고 유지보수하는 일이었다. 제조업이 기반인 쇼핑몰이다 보니 당연히 웹앱일 것이라 생각했는데 의외로 네이티브로 처리하는 부분이 많아 놀랐다. iOS 개발자는 나 혼자였고 안드로이드 개발자가 1명, 나머지는 전부 풀스택 개발자 10명으로 이루어진 팀이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 출근을 하고 인수인계를 받는데 솔직히 좀 어지러웠다. Objective-C, UIKit, SwiftUI가 전부 섞여 있었고 3개의 앱 모두 공통된 레이아웃에 로고나 텍스트만 다른 UI가 많았는데 정작 모듈화는 되어 있지 않았다. 게다가 상세하게 뜯어보면 미묘하게 다른 부분들이 있어서 프로젝트를 파악하는 것이 쉽지 않았다. 참고할 만한 문서도 존재하지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;퇴사하는 iOS 개발자분에게 들어보니 사정이 있었다. 원래는 풀스택 개발자들이 조금씩 공부해가며 iOS와 Android를 개발하다가 모바일은 OS별로 언어도 다르고 디펜던시도 다르다 보니 결국 한계에 부딪혔다고 한다. 그러다 그 일을 맡던 분이 퇴사하면서 유지보수할 사람이 없어지자 협력업체를 끼고 앱 개발자를 채용해 유지보수를 이어오고 있던 것이었다. 그런 과정을 거치다 보니 지금과 같은 상황이 된 것이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 업무를 어느 정도 파악하고 나서 나는 목표를 하나 정했다. 첫째는 세 개의 앱에서 공통되는 부분들을 모듈화하여 유지보수를 용이하게 만드는 것이고, 둘째는 앱 아키텍처를 다시 설계해서 마찬가지로 유지보수가 편한 구조로 바꾸는 것이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;막막하긴 했지만 한편으로는 내가 도전해보고 싶었던 것들을 마음껏 해볼 수 있는 환경이라는 생각도 들었다. 이 목표들을 어떻게 풀어나갔는지는 다음 분기 회고에서 이어서 이야기해보려 한다.&lt;/p&gt;</description>
      <category>회고/일상</category>
      <author>물복딱복준복</author>
      <guid isPermaLink="true">https://junbok97.tistory.com/386</guid>
      <comments>https://junbok97.tistory.com/386#entry386comment</comments>
      <pubDate>Wed, 29 Jul 2026 16:05:02 +0900</pubDate>
    </item>
    <item>
      <title>2024년 2분기 회고</title>
      <link>https://junbok97.tistory.com/385</link>
      <description>&lt;h1 data-heading=&quot;대기업 공채 준비의 시작&quot;&gt;대기업 공채 준비의 시작&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2분기에도 계속 취업 준비를 이어갔다. 한두 군데를 지원하더라도 채용 프로세스가 워낙 길다 보니 시간이 참 빠르게 지나갔던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때는 주로 대기업 공채 위주로 준비했다. 코딩테스트 준비도 올해 처음 해보는 것이었고 자기소개서도 제대로 써본 적이 없다 보니 하루 종일 코딩테스트와 자기소개서만 붙잡고 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이버, 카카오, 올리브영 등 여러 곳에 지원했고 서류 전형을 통과한 뒤에는 코딩테스트를 보고 또 면접을 준비했다. 공채는 채용 프로세스의 텀이 굉장히 길어서 한 곳의 결과를 기다리는 동안 또 다른 곳을 준비하다 보면 어느새 시간이 훌쩍 지나가 있었다.&lt;/p&gt;
&lt;h1 data-heading=&quot;다시 카페 아르바이트를 시작하다&quot;&gt;다시 카페 아르바이트를 시작하다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부스트캠프에 들어오기 전 거의 2년 동안 일했던 카페가 있었는데 그곳에서 취업하기 전까지 다시 일할 생각이 없냐고 연락이 왔다. 사실 부스트캠프를 수료하고 나서도 연락이 왔었지만 그때는 금방 취업할 거라 생각해서 거절했었다. 금방 들어갔다가 다시 나오면 민폐라고 생각했기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 생각보다 취업 시장이 많이 얼어붙어 있었고 채용 프로세스의 텀도 길다 보니 취업 준비 기간이 점점 길어졌다. 그러다 보니 마음도 조금씩 지치게 되었고, 차라리 아르바이트를 하면서 준비하는 것이 낫겠다는 생각이 들어 다시 카페 일을 시작하게 되었다.&lt;/p&gt;
&lt;h1 data-heading=&quot;2분기를 돌아보며&quot;&gt;2분기를 돌아보며&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2분기는 눈에 보이는 성과가 있었다기보다는 묵묵히 준비하며 버틴 시간에 가까웠다. 코딩테스트와 자기소개서를 처음부터 하나씩 익혀갔고, 길어지는 취업 준비 속에서 아르바이트와 병행하는 나름의 방법을 찾은 분기였다. 조급한 마음이 들 때도 있었지만 꾸준히 준비하다 보면 결국엔 기회가 올 것이라 믿으며 하루하루를 보냈다.&lt;/p&gt;</description>
      <category>회고/일상</category>
      <author>물복딱복준복</author>
      <guid isPermaLink="true">https://junbok97.tistory.com/385</guid>
      <comments>https://junbok97.tistory.com/385#entry385comment</comments>
      <pubDate>Wed, 29 Jul 2026 16:04:49 +0900</pubDate>
    </item>
    <item>
      <title>2024년 1분기 회고</title>
      <link>https://junbok97.tistory.com/384</link>
      <description>&lt;h1 data-heading=&quot;취업 준비의 시작, 그리고 얼어붙은 시장&quot;&gt;취업 준비의 시작, 그리고 얼어붙은 시장&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부스트캠프를 수료하고 나서 본격적으로 취업 준비를 시작했다. 이력서를 작성하고 코딩테스트와 기술 면접을 준비하며 하루하루를 보냈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 부스트캠프와 취업 사이에서 부스트캠프를 선택했던 이유 중 하나는 대기업과의 취업 연계가 있었기 때문이었다. 그런데 막상 내가 수료했을 때는 상황이 좋지 않았다. 코로나 시기가 끝나면서 IT 업계들이 신입 채용을 하지 않는 시기와 맞물려 기대했던 채용 연계가 없어져버린 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 iOS 개발자는 pool 자체가 작다 보니 신입, 주니어, 시니어 할 것 없이 채용 공고 자체가 적었고 그나마 있는 것들도 대부분 미들, 시니어 공고였다. 그래도 나는 주니어와 미들 개발자 공고에 지원했고 그중 몇 곳은 면접까지 볼 수 있었다.&lt;/p&gt;
&lt;h1 data-heading=&quot;첫 과제 전형, 리멤버&quot;&gt;첫 과제 전형, 리멤버&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1분기에 가장 기억에 남는 면접은 리멤버라는 기업이었다. 과제 전형이라는 것을 처음 해보았는데 주어진 일주일이라는 시간이 굉장히 짧게 느껴졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 과제의 조건 자체는 어렵지 않았다. 그래서 오히려 고민이 더 많았다. 현업을 경험해보지 못하다 보니 어떻게 설계를 해야 하는지, 또 회사에서 어떤 것을 중점적으로 보는지를 알 수 없었기 때문이다. 한참을 고민하다가 결국 가장 심플하게 개발하기로 했고 다행히 과제 전형은 통과할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이어서 기술 면접을 보러 갔는데 iOS 팀 리드분이 부스트캠프 멘토를 해보신 분이어서 면접 자체에 어려운 것은 없었다. 기술적인 부분은 다 아는 것들이었다. 오히려 과제 전형과 관련된 질문들이 더 어려웠다. 실무를 경험해보지 못하다 보니 예외 처리를 왜 이렇게 했는지, 이렇게 하면 사용자 경험은 어떨 것 같은지 등 내가 평소에 생각해보지 못한 부분들을 물어보셔서 조금 당황했다. 결국 나는 솔직하게 거기까지는 생각하지 못했다고 답변드렸다. 이 외에는 회사 문화나 내가 궁금한 점들을 여쭤보며 면접을 마쳤다.&lt;/p&gt;
&lt;h1 data-heading=&quot;탈락, 그리고 뜻밖의 커피챗&quot;&gt;탈락, 그리고 뜻밖의 커피챗&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일주일 뒤에 결과가 나왔는데 탈락이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 결과를 받고 2~3주 정도 지났을 무렵 당시 면접을 봤던 리드님이 커피챗을 제안해주셨다. 커피챗을 하면서 리드님이 해주신 말씀이 오래 기억에 남는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;면접을 봐왔던 사람들 중에서 내가 가장 좋았고, 대답이 아쉬웠던 것들은 신입이다 보니 경험해보지 못해서 그런 것 같으니 경험을 쌓으면 충분히 좋은 대답을 했을 것 같다고 하셨다. 웬만한 주니어 못지않았지만 지금 회사와 팀에서 원하는 것은 미들급 팀원이라 공고도 미들급으로 다시 올렸다고 하셨다. 요즘 IT 업계가 전반적으로 불황이라 주니어를 뽑아서 성장시키는 데 자원을 들이는 것보다 이미 경험이 있는 사람을 뽑으려 하다 보니 미안하다는 말씀도 덧붙이셨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 충분히 이해한다고 답변드렸다. 그리고 반대로 지금 내 이력서나 면접에서 무엇을 더 해야 할지 모르겠는 상황이라 어떻게 해야 할지를 여쭤보았다. 리드님은 iOS 개발에서 여러 가지를 해보는 것을 추천해주셨다. 하나를 깊게 파는 것은 이미 해봤으니 이제는 다양하게 여러 개를 해보라는 조언이었다.&lt;/p&gt;
&lt;h1 data-heading=&quot;1분기를 돌아보며&quot;&gt;1분기를 돌아보며&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1분기는 기대했던 채용 연계도 없고 시장 상황마저 좋지 않아 마음처럼 풀리지 않은 시간이었다. 하지만 리멤버 면접과 뒤이은 커피챗을 통해 내가 지금 어느 정도의 위치에 있는지, 그리고 앞으로 어떤 방향으로 나아가야 할지에 대한 힌트를 얻을 수 있었다. 리드님이 해주신 조언대로 이제는 하나를 깊게 파기보다 다양한 것들을 경험해보며 부족한 부분을 채워나가야겠다고 다짐한 분기였다.&lt;/p&gt;</description>
      <category>회고/일상</category>
      <author>물복딱복준복</author>
      <guid isPermaLink="true">https://junbok97.tistory.com/384</guid>
      <comments>https://junbok97.tistory.com/384#entry384comment</comments>
      <pubDate>Wed, 29 Jul 2026 16:04:34 +0900</pubDate>
    </item>
    <item>
      <title>네이버 부스트캠프 웹&amp;middot;모바일 8기 최종 회고</title>
      <link>https://junbok97.tistory.com/383</link>
      <description>&lt;h1 data-heading=&quot;6개월을 마치며&quot;&gt;6개월을 마치며&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그룹프로젝트 발표를 끝으로 6개월간 이어온 네이버 부스트캠프를 모두 수료하게 되었다. 그룹프로젝트 결과물을 공유하는 날이기도 했지만 무엇보다 긴 여정을 마무리하는 날이었기에 정말 감명 깊었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6개월 동안 주 5일 하루 8시간이 넘는 시간을 기술적 고민과 개발, 그리고 캠퍼들과의 소통으로 보냈는데 이 시간들이 평생 기억에 남을 것 같다.&lt;/p&gt;
&lt;h1 data-heading=&quot;취업 VS 네이버 부스트캠프&quot;&gt;취업 VS 네이버 부스트캠프&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 목표로 했던 것들을 이루고 난 뒤 취업 준비를 하며 최종 면접까지 볼 수 있었고 결과를 기다리던 중 네이버 부스트캠프의 모집 공고를 보게 되었다. 네이버 부스트캠프가 워낙 유명하여 궁금하기도 했고 다른 iOS 개발자들을 만나본 경험이 없었기 때문에 회사에서 불합격한다면 한번 해봐야지 하는 생각으로 지원하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 운이 좋게도 회사에도 합격을 하고 부스트캠프에도 합격을 하게 되어 고민이 깊어졌다. 현실적으로 신입 개발자로서 취업이 어려운 상황에서 합격한 기회를 두고 기초를 다지는 데 풀타임으로 6개월이라는 시간을 쓰는 것이 맞을까 하는 생각이 들었기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 반대로 지금이 아니고서는 기초를 다지는 데 이 정도로 시간을 쏟을 기회가 없을 것이라 생각했다. 지금은 6개월이 길게 느껴지겠지만 지나고 나서 보았을 때 이 시간이 더 좋은 iOS 개발자가 되는 밑거름이 되어줄 것이라 생각하고 결국 네이버 부스트캠프를 선택하게 되었다.&lt;/p&gt;
&lt;h1 data-heading=&quot;후회 없는 선택&quot;&gt;후회 없는 선택&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이버 부스트캠프를 수료하고 나서 돌아보니 이 선택은 내가 할 수 있었던 최고의 선택이었다. 그동안 내가 몰랐던, 내게 부족했던 부분들을 알 수 있었고 부스트캠프에서만 경험할 수 있는 것들과 같은 꿈을 가진 동료들과 하나의 목표를 향해 달려가는 경험이 너무나 좋았으며 좋은 습관들도 많이 배우게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 기억에 남는 것은 페어 프로그래밍과 코드 리뷰였다. 페어 프로그래밍과 코드 리뷰를 통해 코드를 작성할 때 한번 더 사이드 이펙트나 이슈는 없는지 고민하게 되었고, 서로 의견이 다를 때 커뮤니케이션을 통해 더 좋은 방법을 도출해내는 경험을 할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부스트캠프는 처음부터 쉬운 것이 하나도 없었다. 대부분이 처음 접해보는 주제와 기술들이었고 이를 어떻게 해결해야 하나 하는 막막함뿐이었다. 모든 게 힘들고 어려웠지만 나는 포기하지 않았고 조금씩 꾸준히 목표를 세워가며 때론 동료에게 도움을 받고 반대로 도움을 주며 결국에는 주어진 미션들을 해결할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;꾸준히 하다 보면 결국엔 해낼 수 있다.&quot; 나의 가치관이자 삶의 스탠스다. 처음 개발을 접했을 때는 너무나 어려워서 공식 문서조차 읽지 못했지만 지금은 공식 문서뿐 아니라 다른 사람들의 코드나 오픈소스 코드를 읽고 이해하는 데 큰 어려움이 없을 정도로 성장했다. 무언가를 꾸준히 하여 결국엔 해낼 수 있다는 경험들은 앞으로 어떤 어려운 문제가 생겨도 주저앉지 않고 해결할 수 있는 원동력이 될 것이다.&lt;/p&gt;</description>
      <category>회고/네이버 부스트캠프</category>
      <author>물복딱복준복</author>
      <guid isPermaLink="true">https://junbok97.tistory.com/383</guid>
      <comments>https://junbok97.tistory.com/383#entry383comment</comments>
      <pubDate>Wed, 29 Jul 2026 16:03:15 +0900</pubDate>
    </item>
    <item>
      <title>부스트캠프 웹&amp;middot;모바일 8기 그룹프로젝트 6주차 회고</title>
      <link>https://junbok97.tistory.com/382</link>
      <description>&lt;h1 data-heading=&quot;6주간의 결과물을 공유하는 네트워킹 데이&quot;&gt;6주간의 결과물을 공유하는 네트워킹 데이&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드디어 6주간 개발한 결과물을 공유하는 네트워킹 데이가 되었다. 네트워킹 데이는 오프라인으로 진행되었고 iOS 파트, Android 파트, Frontend 파트 모두가 모여 각자 6주 동안 개발한 결과물을 공유하는 시간이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 기간 동안 각 파트가 어떤 고민을 하고 어떤 것들을 만들어왔는지 직접 보고 들을 수 있어서 정말 의미 있는 시간이었다. 우리 팀이 만든 것을 다른 파트 사람들에게 보여주는 것도 뿌듯했고 다른 팀들이 만든 결과물을 보면서 자극을 받기도 했다.&lt;/p&gt;
&lt;h1 data-heading=&quot;현업 개발자분들과의 커피챗&quot;&gt;현업 개발자분들과의 커피챗&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워킹 데이에는 실제 현업 개발자분들과 멘토분들이 오셔서 커피챗을 하는 시간도 가졌다. 취업에 관한 질문이나 현업에서는 실제로 어떻게 개발을 하는지에 대해 편하게 물어볼 수 있는 자리였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 취업 준비와 면접 준비는 어떻게 해야 할지를 여쭤봤는데, 부스트캠프를 수료했으니 실력은 어느 정도 보장된 것이라 생각한다는 이야기를 많이 들었다. 요즘 시장이 어렵긴 하지만 이력서와 자기소개서만 잘 준비하면 취업이 그렇게 어렵지는 않을 것이라는 말씀도 해주셨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부스트캠프를 하면서 계속 궁금했던 현업의 이야기를 직접 들을 수 있어서 좋았고 앞으로 취업을 준비하면서 어떤 부분을 더 신경 써야 할지에 대해서도 많은 힌트를 얻을 수 있었다.&lt;/p&gt;
&lt;h1 data-heading=&quot;이제 정말 끝이라니, iOS 챕터 회식&quot;&gt;이제 정말 끝이라니, iOS 챕터 회식&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워킹 데이가 끝난 다음에는 iOS 챕터끼리 모여서 회식을 했다. 6개월 동안 동고동락하다 보니 어느새 많이 친해졌는데 이제 정말 끝이라고 하니 좀처럼 믿기지가 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 그동안 있었던 오프라인 모임 두 번을 모두 내가 주최했었는데 다들 그 부분에 대해 고맙다고 말해주어서 뿌듯했다. 부스트캠프가 완전히 온라인으로 진행되다 보니 부산, 광주처럼 서울에서 멀리 떨어져 사는 분들도 있었는데 오프라인 모임을 할 때마다 항상 먼 길을 마다하지 않고 와주셔서 정말 고마웠다.&lt;/p&gt;
&lt;h1 data-heading=&quot;6주간의 그룹프로젝트를 마치며&quot;&gt;6주간의 그룹프로젝트를 마치며&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;발표를 끝으로 6주간의 그룹프로젝트가 마무리되었다. RIBs와 Tuist, 모듈화처럼 처음 도전해보는 것들이 많아 매주 설계를 갈아엎고 트러블슈팅에 시달리기도 했지만 그만큼 짧은 기간에 정말 많이 성장할 수 있었던 시간이었다. 팀원들과 하나의 서비스를 끝까지 완성해봤다는 것만으로도 큰 자산이 된 프로젝트였다.&lt;/p&gt;</description>
      <category>회고/네이버 부스트캠프</category>
      <author>물복딱복준복</author>
      <guid isPermaLink="true">https://junbok97.tistory.com/382</guid>
      <comments>https://junbok97.tistory.com/382#entry382comment</comments>
      <pubDate>Wed, 29 Jul 2026 16:03:03 +0900</pubDate>
    </item>
    <item>
      <title>부스트캠프 웹&amp;middot;모바일 8기 그룹프로젝트 5주차 회고</title>
      <link>https://junbok97.tistory.com/381</link>
      <description>&lt;h1 data-heading=&quot;부스트캠프 웹&amp;middot;모바일 8기 그룹프로젝트 5주차 회고&quot;&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1 data-heading=&quot;다른 사람의 작업 이어하기&quot;&gt;다른 사람의 작업 이어하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발을 시작할 당시 우리는 화면 단위로 작업을 나누어서 개발을 진행했다. 그런데 마스터 클래스에서 이렇게 화면을 나누어 개발하는 건 다른 곳에서도 얼마든지 할 수 있으니 페어프로그래밍 같은 것을 한번 고민해보라는 이야기를 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀원들끼리 이야기한 결과 우리는 서로 작업하던 영역을 바꾸어서 이어서 개발해보기로 했다. 그동안 PR을 통해 다른 사람의 코드를 어느 정도 이해하고 있다고 생각했지만 사실 완벽하게 이해하고 있는 것은 아니었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;막상 작업을 바꾸어서 남이 개발하던 부분을 직접 이어받아 개발해보니 PR만 볼 때와는 이해의 깊이가 완전히 달랐다. 코드를 눈으로 읽는 것과 직접 손대서 수정하는 것은 다르다는 걸 체감했고 덕분에 프로젝트 전체 구조에 대한 이해도가 굉장히 많이 향상되었다.&lt;/p&gt;
&lt;h1 data-heading=&quot;프로필 수정은 어떤 로직이 좋을까 ??&quot;&gt;프로필 수정은 어떤 로직이 좋을까 ??&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 주에는 프로필 수정 기능을 개발하면서 로직에 대한 고민을 했다. 프로필 수정 화면에 들어갔을 때 이미지나 유저 이름을 변경할 수도 있고 변경하지 않을 수도 있는데 이 경우들을 어떻게 처리할지가 관건이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지는 변경하지 않았을 때 서버에서 굳이 수정하지 않도록 null 값을 주어 요청하는 방식으로 처리했다. 유저 이름은 조금 더 신경을 써야 했는데 우선 중복 검사를 실시하고 변경하려는 이름이 원래 본인의 닉네임과 동일할 때는 null 값을 던지도록 수정했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 입력받은 값을 그대로 서버로 보내는 것이 아니라 변경 여부에 따라 무엇을 보내고 무엇을 보내지 않을지를 정하는 과정에서 클라이언트와 서버가 어떻게 역할을 나누어야 하는지를 다시 한번 생각하게 되었다.&lt;/p&gt;
&lt;h1 data-heading=&quot;우리의 기술적 도전은 무엇일까 ??&quot;&gt;우리의 기술적 도전은 무엇일까 ??&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 주에 가장 많이 한 고민이다. 앱이 초기에 계획했던 것들이 큰 무리 없이 개발되어 있는 상태였는데 문득 과연 우리의 기술적 도전이 무엇인가 하는 의문이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 RIBs와 클린 아키텍처를 사용하는 것이 기술적 도전인 걸까 ?? 아니면 기획한 것들의 완성도를 기한 내에 완성하는 것이 기술적 도전인 걸까 ?? 계속 생각해봐도 명확한 답이 나오지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀원들과 상의한 결과 우리는 클러스터링과 애니메이션처럼 앱의 퀄리티를 높이는 작업을 해보기로 했다. 기획한 기능을 빠르게 완성하는 것도 물론 의미가 있지만 그것만으로는 부스트캠프에서만 할 수 있는 도전이라고 하기엔 아쉬웠기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과연 기획한 대로 빠르게 개발하는 것이 좋은 것인가, 그리고 그것이 부스트캠프에서 정말 얻어갈 만한 것인가 하는 생각을 이번 주 내내 하게 되었다. 남은 기간에는 단순히 기능을 완성하는 것을 넘어서 우리만의 도전을 하나씩 만들어가야겠다고 다짐했다.&lt;/p&gt;</description>
      <category>회고/네이버 부스트캠프</category>
      <author>물복딱복준복</author>
      <guid isPermaLink="true">https://junbok97.tistory.com/381</guid>
      <comments>https://junbok97.tistory.com/381#entry381comment</comments>
      <pubDate>Wed, 29 Jul 2026 16:02:50 +0900</pubDate>
    </item>
    <item>
      <title>부스트캠프 웹&amp;middot;모바일 8기 그룹프로젝트 4주차 회고</title>
      <link>https://junbok97.tistory.com/380</link>
      <description>&lt;h1 data-heading=&quot;점점 깊어져가는 뷰들&quot;&gt;점점 깊어져가는 뷰들&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RIBs 아키텍처를 사용하다 보니 뷰를 각각의 리블렛으로 나누게 되었는데 이러다 보니 뷰의 깊이가 점점 깊어지는 문제가 생겼다. 깊이가 깊어지니 하위 리블렛에서 발생한 이벤트를 상위 리블렛으로 보내고 그 상위 리블렛에서 또 상위로 보내는 식으로 이벤트를 계속 타고 올려보내야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분을 stream을 따로 두어서 해결할까도 고민했지만 데이터 전달이 stream을 둘 만큼 자주 일어나지는 않아서 우선은 지금처럼 타고타고 올라가는 방식으로 두는 것이 최선이라고 판단했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 이렇게 이벤트가 여러 리블렛을 거쳐서 올라가다 보니 내가 작성한 코드는 흐름을 알 수 있어도 남이 작성한 코드를 보면 이벤트가 어디서 시작해서 어디로 흘러가는지 한눈에 파악하기가 힘들었다. RIBs로 책임을 잘게 나눈 대신 전체 흐름을 읽는 비용이 늘어난 셈이라 이 트레이드오프를 어떻게 다뤄야 할지 계속 고민하게 되었다.&lt;/p&gt;
&lt;h1 data-heading=&quot;옳바르지 않은 유저 ?? 원인은 http였다&quot;&gt;옳바르지 않은 유저 ?? 원인은 http였다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 주에는 정말 골치 아픈 버그를 만났다. 그동안 서버와의 통신이 잘 되다가 토큰 만료 로직을 추가한 뒤부터 옳바르지 않은 유저라는 에러가 발생하기 시작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 이 에러가 일관적이지 않다는 것이었다. 잘 동작하는 팀원도 있고 나처럼 아예 동작하지 않거나 잘 될 때도 있고 안 될 때도 있어서 도대체 왜 발생하는지 찾기가 너무 힘들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전날 secret 파일에서 key값을 수정한 것이 있어서 그게 원인인 줄 알고 팀원끼리 secret을 교환해보았다. 그런데 교환하고 난 뒤에도 잘 동작하는 팀원이 있고 동작하지 않는 팀원이 있어서 도무지 원인을 찾을 수가 없었다. 팀원들이 다 모여서 서버와 클라이언트를 전부 체크해보았지만 어디서 에러가 발생하는지를 쉽게 찾지 못했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한참을 헤매다가 알고 보니 key값이 문제가 아니라 서버 url이 https여야 하는데 http로 되어있어서 발생하는 문제였다. 서버 url을 http에서 https로 바꿔주니 그동안 나를 괴롭히던 에러가 거짓말처럼 사라지고 아주 잘 동작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러가 이렇게 일관되지 않게 발생할 때는 gitignore한 파일들을 잘 확인해보는 것이 중요하다는 것을 이번 기회에 확실히 알게 되었다. 정작 토큰 만료 로직 자체는 멀쩡했는데 엉뚱한 곳에서 원인을 찾느라 시간을 많이 썼던 것이 아쉬우면서도 기억에 오래 남을 것 같다.&lt;/p&gt;</description>
      <category>회고/네이버 부스트캠프</category>
      <author>물복딱복준복</author>
      <guid isPermaLink="true">https://junbok97.tistory.com/380</guid>
      <comments>https://junbok97.tistory.com/380#entry380comment</comments>
      <pubDate>Wed, 29 Jul 2026 16:02:30 +0900</pubDate>
    </item>
    <item>
      <title>부스트캠프 웹&amp;middot;모바일 8기 그룹프로젝트 3주차 회고</title>
      <link>https://junbok97.tistory.com/379</link>
      <description>&lt;h1 data-heading=&quot;부스트캠프 웹&amp;middot;모바일 8기 그룹프로젝트 3주차 회고&quot;&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1 data-heading=&quot;검색 기능, 설계를 몇 번이나 바꿨다&quot;&gt;검색 기능, 설계를 몇 번이나 바꿨다&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 주에는 검색 기능을 맡아서 개발했다. 검색은 단순히 검색어를 입력하고 결과를 보여주는 것이라 생각해서 어렵지 않을 줄 알았다. 하지만 막상 개발을 시작하니 검색어를 입력하기 전, 입력하는 중, 입력하고 난 뒤에 각각 다른 화면을 보여줘야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음엔 이걸 하나의 RIB으로 처리하려고 했다. 굳이 나누지 않아도 상태만 바꿔주면 되겠다고 생각했기 때문이다. 그런데 상태별로 다뤄야 하는 데이터와 UI가 늘어나다 보니 하나의 RIB에 너무 많은 책임이 몰리게 되었다. 정작 모듈 분리와 책임 분리를 위해 RIBs를 선택한 것인데 오히려 massive한 RIB이 되어버린 것이다. 내가 RIBs를 쓰는 이유와 정반대의 결과가 나온 셈이라 여기서 설계를 다시 하기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 검색을 총괄하는 Container RIB을 두고 상태별로 Sub RIB들을 추가해서 나누는 방향으로 개발했다. 책임이 명확하게 쪼개지니 각 RIB이 무슨 일을 하는지가 분명해져서 좋았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이렇게 나누고 나니 또 다른 의문이 생겼다. 자식 RIB들끼리 데이터를 전달하려면 Container RIB을 거쳐서 전달해줘야 했는데 RIB이 많아질수록 Container를 통해 데이터를 주고받는 코드도 계속 늘어났다. 이게 과연 좋은 개발 방식인가 하는 의문이 계속 들었다. 책임을 나누기 위해 RIB을 쪼갰는데 그 대가로 데이터 전달이 복잡해지는 트레이드오프가 있어서 어느 지점까지 나누는 것이 적절한지에 대한 고민을 계속하게 되었다.&lt;/p&gt;
&lt;h1 data-heading=&quot;인터페이스와 구현체 나누기&quot;&gt;인터페이스와 구현체 나누기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 주 또 다른 기술적 고민은 모듈을 나누는 것이었다. 우리는 모듈화를 진행하면서 인터페이스와 구현체의 모듈을 나누기로 했는데 인터페이스만 노출하고 구현체는 숨기려고 하니 이 인터페이스를 어떻게 설계해야 할지가 고민이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터페이스를 너무 잘게 나누면 사용하는 쪽에서 매번 여러 개를 조합해서 써야 해서 불편했고 반대로 너무 크게 잡으면 구현체가 인터페이스에 끌려다니게 되어서 모듈을 나눈 의미가 옅어졌다. 어느 정도의 크기로 인터페이스를 잡아야 사용하는 쪽도 편하고 구현체도 유연하게 가져갈 수 있을지 계속해서 고민하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 정답을 찾았다고 할 수는 없지만 이런 고민을 직접 해보면서 모듈을 나눈다는 것이 단순히 파일을 폴더별로 나누는 것이 아니라는 걸 체감하게 되었다.&lt;/p&gt;
&lt;h1 data-heading=&quot;빠른 팀원, 느린 나&quot;&gt;빠른 팀원, 느린 나&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 이번 주는 기술적인 부분보다 마음이 더 힘들었다. 이번 주 내 개발 속도가 많이 느려진 것 같았다. 앞서 말했듯이 검색 기능을 개발하면서 설계를 계속해서 바꿨던 것이 가장 큰 이유였다. 하나의 RIB으로 시작했다가 massive해져서 다시 Container와 Sub RIB으로 나누는 과정을 거치다 보니 같은 기능을 몇 번이나 갈아엎게 되었다. 새로운 기능을 개발할 때 공부해가면서 개발을 하다 보니 사소한 것에도 시간을 많이 쓰게 되었고 그러다 보니 다른 팀원들에게 부담을 주는 것은 아닐까 하는 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업을 분배할 때 내 작업이 시간이 오래 걸릴 것이라는 건 알고 있었지만 생각보다 더 오래 걸렸다. 그렇다 보니 개발 속도가 빠른 팀원이 많은 기능을 개발하거나 프로젝트 셋팅을 도맡아서 하는 일이 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 고민이 생겼다. 빠르게 개발이 가능한 사람이 다 도맡아서 개발하는 것이 맞는 것일까 ... ? 당장 프로젝트를 빠르게 진행하는 것만 생각하면 잘하는 사람이 많이 하는 것이 맞겠지만 그렇게 되면 나는 성장할 기회를 잃게 되고 결국 팀 전체로 봐도 좋은 방향은 아니라고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 나는 조금 느리더라도 내가 맡은 부분은 끝까지 스스로 해내되 다른 사람에게 부담을 주지 않도록 조금 더 속도감 있게 개발하자고 다짐했다. 느린 것이 부끄러운 것이 아니라 느린데도 아무것도 하지 않는 것이 부끄러운 것이라 생각하며 이번 주를 마무리했다.&lt;/p&gt;</description>
      <category>회고/네이버 부스트캠프</category>
      <author>물복딱복준복</author>
      <guid isPermaLink="true">https://junbok97.tistory.com/379</guid>
      <comments>https://junbok97.tistory.com/379#entry379comment</comments>
      <pubDate>Wed, 29 Jul 2026 16:02:14 +0900</pubDate>
    </item>
    <item>
      <title>[Swift] RxSwift의 subscribe(on:)와 observe(on:), Combine의 subscribe(on:)와 receive(on:)</title>
      <link>https://junbok97.tistory.com/378</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;rxSwift와 combine의 비동기 흐름은 아래와 같다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Observable&amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;rarr;&amp;nbsp; &amp;nbsp; &amp;nbsp;이벤트 생성&amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;rarr;&amp;nbsp; &amp;nbsp; &amp;nbsp;전달&amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;rarr;&amp;nbsp; &amp;nbsp; &amp;nbsp;Observer&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;RxSwift의 경우&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Observable&amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;rarr;&amp;nbsp; &amp;nbsp; &amp;nbsp;이벤트 생성&amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;rarr;&amp;nbsp; &amp;nbsp; &amp;nbsp;전달&amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;rarr;&amp;nbsp; &amp;nbsp; &amp;nbsp;Observer&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; subscribe(on:)&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; observe(on:)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트가 생성되는 쪽의 실행 스레드를 지정해주는 것이 subscribe(on:)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성된 이벤트를 전달받아 처리하는 쪽의 실행 스레드를 지정해주는 것이 observe(on:)이다.&lt;/p&gt;
&lt;pre id=&quot;code_1750422758995&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Observable&amp;lt;Int&amp;gt;
     .create { observer in
         // Subscription 영역
     }
     .subscribe(on:) // Subscription 영역의 실행 스레드 지정 (업스트림)
     .observe(on:) // Observing 영역을 지정 (다운 스트림)
     .subscribe { _ in
         // Observing 영역
     }
     .disposed(by: disposeBag)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;subscribe(on:)은 어느곳에서 호출되더라도 이벤트를 방출하는 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Subscription영역의 실행 스레드를 변경시키고&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;observe(on:)은 Observing 영역의 실행스레드를 변경시키기 때문에 호출된 곳의 아래 있는 실행스레드들이 바뀌게 된다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kwA0q/btsOLjwPo5K/J6U08Jcl5FoSLWiqMmArzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kwA0q/btsOLjwPo5K/J6U08Jcl5FoSLWiqMmArzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kwA0q/btsOLjwPo5K/J6U08Jcl5FoSLWiqMmArzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkwA0q%2FbtsOLjwPo5K%2FJ6U08Jcl5FoSLWiqMmArzk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;1600&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1600&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;subscribe(on:)은 여러번 호출되더라도&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Subscription영역은 한군데이기 때문에 위치와 관계 없이 가장 처음 호출된것이 적용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;observe(on:)은 다른 observe(on:)을 만날때 까지 적용된다&lt;/p&gt;
&lt;pre id=&quot;code_1750423398415&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Observable&amp;lt;Int&amp;gt;
    .create { observer in
        // Subscription 영역
        print(&quot;create 스레드: \(Thread.current)&quot;)
        observer.onNext(1)
        observer.onCompleted()
        return Disposables.create()
    }
    .subscribe(on: ConcurrentDispatchQueueScheduler(qos: .background))  // 이것만 적용    
    .observe(on: ConcurrentDispatchQueueScheduler(qos: .background)) // 아래 전부 적용
    .map {
        // Observing 영역
        print(&quot;Map 스레드: \(Thread.current)&quot;)
        return $0
    }
    .subscribe(on: MainScheduler.instance)
    .observe(on: MainScheduler.instance) // 아래 전부 적용
    .subscribe { _ in
        // Observing 영역
        print(&quot;subscribe 스레드: \(Thread.current)&quot;)
    }
    .disposed(by: disposeBag)
    
    
Observable&amp;lt;Int&amp;gt;
    .create { observer in
        // Subscription 영역
        print(&quot;create 스레드: \(Thread.current)&quot;)
        observer.onNext(1)
        observer.onCompleted()
        return Disposables.create()
    }
    .subscribe(on: ConcurrentDispatchQueueScheduler(qos: .background))  // 이것만 적용
    .subscribe(on: MainScheduler.instance)
    .observe(on: ConcurrentDispatchQueueScheduler(qos: .background)) // 아래 전부 적용
    .map {
        // Observing 영역
        print(&quot;Map 스레드: \(Thread.current)&quot;)
        return $0
    }
    .observe(on: MainScheduler.instance) // 아래 전부 적용
    .subscribe { _ in
        // Observing 영역
        print(&quot;subscribe 스레드: \(Thread.current)&quot;)
    }
    .disposed(by: disposeBag)
    

// 결과
create 스레드: &amp;lt;NSThread: 0x600001750c00&amp;gt;{number = 5, name = (null)}
Map 스레드: &amp;lt;NSThread: 0x600001750c00&amp;gt;{number = 5, name = (null)}
subscribe 스레드: &amp;lt;_NSMainThread: 0x60000170c000&amp;gt;{number = 1, name = main}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Combine의 경우&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Observable&amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;rarr;&amp;nbsp; &amp;nbsp; &amp;nbsp;이벤트 생성&amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;rarr;&amp;nbsp; &amp;nbsp; &amp;nbsp;전달&amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;rarr;&amp;nbsp; &amp;nbsp; &amp;nbsp;Observer&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; subscribe(on:)&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;receive(on:)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;combine도 rxSwift와 동일하다 다만 이름이 observe(on:) -&amp;gt; receive(on:)로 변경된것 뿐이다.&lt;/p&gt;
&lt;pre id=&quot;code_1750424278030&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Just(1)
    .subscribe(on: DispatchQueue.global()) // 이것만 적용
    .subscribe(on: RunLoop.main)
    .handleEvents(receiveSubscription: { _ in
        print(&quot;subscription 스레드 \(Thread.current)&quot;)
    })
    .receive(on: RunLoop.main)
    .map { (value) -&amp;gt; Int in
        print(&quot;map 스레드 \(Thread.current)&quot;)
        return value
    }
    .receive(on: DispatchQueue.global())
    .sink { value in
        print(&quot;received 스레드 \(Thread.current)&quot;)
    }
    
Just(1)
    .handleEvents(receiveSubscription: { _ in
        print(&quot;subscription 스레드 \(Thread.current)&quot;)
    })
    .receive(on: RunLoop.main)
    .map { (value) -&amp;gt; Int in
        print(&quot;map 스레드 \(Thread.current)&quot;)
        return value
    }
	.subscribe(on: DispatchQueue.global()) // 이것만 적용
    .subscribe(on: RunLoop.main)
    .receive(on: DispatchQueue.global())
    .sink { value in
        print(&quot;received 스레드 \(Thread.current)&quot;)
    }
    
// 결과
subscription 스레드 &amp;lt;NSThread: 0x6000017bf700&amp;gt;{number = 9, name = (null)}
map 스레드 &amp;lt;_NSMainThread: 0x600001710000&amp;gt;{number = 1, name = main}
received 스레드 &amp;lt;NSThread: 0x600001718300&amp;gt;{number = 7, name = (null)}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;요약&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이벤트 생성 시점 스레드 지정 (가장 처음 호출된 것만 적용 / 업스트림)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2517&quot; data-start=&quot;2475&quot;&gt;subscribe(on:)&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이벤트 처리 시점 스레드 지정 (호출된곳에서부터 아래로 전부 영향을 받음 / 다운스트림)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-start=&quot;2520&quot; data-end=&quot;2594&quot;&gt;RxSwift - observe(on:)&lt;/li&gt;
&lt;li data-start=&quot;2520&quot; data-end=&quot;2594&quot;&gt;Combine - receive(on:)&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>iOS</category>
      <author>물복딱복준복</author>
      <guid isPermaLink="true">https://junbok97.tistory.com/378</guid>
      <comments>https://junbok97.tistory.com/378#entry378comment</comments>
      <pubDate>Fri, 20 Jun 2025 21:59:24 +0900</pubDate>
    </item>
    <item>
      <title>[Swift] @Published는 값을 언제 방출할까 ?</title>
      <link>https://junbok97.tistory.com/377</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;combine에서 @Published는 값이 바뀔때마다 방출을 하는데 이때 방출되는 시점은 언제일까 ??&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연히 didSet이 호출되어 값이 바뀌고 난 뒤라고 생각했는데 실제 값과 receive된 값이 다르다는 걸 알게 되었다.&lt;/p&gt;
&lt;pre id=&quot;code_1750420740871&quot; class=&quot;swift&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//
//  ContentView.swift
//  Test
//
//  Created by 이준복 on 6/20/25.
//

import SwiftUI
import Combine

struct ContentView: View {
    
    @StateObject
    private var viewModel = ViewModel()
    
    var body: some View {
        VStack {
            Text(&quot;Count: \(viewModel.count)&quot;)
                .font(.largeTitle)
            
            Button(&quot;Increment&quot;) {
                viewModel.increment()
            }
        }
        .padding()
    }
    
}

#Preview {
    ContentView()
}


class ViewModel: ObservableObject {
    
    @Published
    var count: Int = 0 {
        willSet {
            print(&quot;willSet: \(count)&quot;)
        }
        
        didSet {
            print(&quot;didSet: \(count)&quot;)
        }
    }
    
    private var cancellables = Set&amp;lt;AnyCancellable&amp;gt;()
    
    init() {
        $count
            .sink { [weak self] value in
                print(&quot;receive: \(value)&quot;, &quot;real: \(self?.count ?? -1)&quot;)
            }
            .store(in: &amp;amp;cancellables)
    }

    func increment() {
        print(&quot;변경 전: \(count)&quot;)
        count += 1
        print(&quot;변경 후: \(count)&quot;)
    }
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드를 실행하기 전 결과를 예상 했을땐 아래라고 생각했는데 실제 결과 값은 그렇지 않았다.&lt;/p&gt;
&lt;pre id=&quot;code_1750421204592&quot; class=&quot;swift&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;swift&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;변경 전: 0
willSet: 0
didSet: 1
receive: 1 real: 0
변경 후: 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실제 결과 값&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3248&quot; data-origin-height=&quot;1988&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfjpY9/btsOLPPvhnT/kHPaekK0FsrIKpfn2GZ6nK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfjpY9/btsOLPPvhnT/kHPaekK0FsrIKpfn2GZ6nK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfjpY9/btsOLPPvhnT/kHPaekK0FsrIKpfn2GZ6nK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfjpY9%2FbtsOLPPvhnT%2FkHPaekK0FsrIKpfn2GZ6nK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3248&quot; height=&quot;1988&quot; data-origin-width=&quot;3248&quot; data-origin-height=&quot;1988&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 찾아보니 willSet에서 .send(newValue)를 호출하여 값을 방출하는 것 같았다. 실제 메모리 반영되고 값이 바뀐 시점은 didSet 시점이니 이 부분을 알고 있으면 좋을 것 같다.&lt;/p&gt;</description>
      <category>iOS</category>
      <author>물복딱복준복</author>
      <guid isPermaLink="true">https://junbok97.tistory.com/377</guid>
      <comments>https://junbok97.tistory.com/377#entry377comment</comments>
      <pubDate>Fri, 20 Jun 2025 21:08:58 +0900</pubDate>
    </item>
  </channel>
</rss>