<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Session on Kelly Dev</title><link>https://kelly-chui.github.io/tags/session/</link><description>Recent content in Session on Kelly Dev</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Mon, 10 Feb 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://kelly-chui.github.io/tags/session/index.xml" rel="self" type="application/rss+xml"/><item><title>SniffMEET. AuthManager와 SessionManager의 책임 분리하기</title><link>https://kelly-chui.github.io/posts/sniffmeet-dev-log-auth-session-responsibilities/</link><pubDate>Mon, 10 Feb 2025 00:00:00 +0000</pubDate><guid>https://kelly-chui.github.io/posts/sniffmeet-dev-log-auth-session-responsibilities/</guid><description>&lt;h2 id="작업-배경"&gt;작업 배경&lt;/h2&gt;
&lt;p&gt;AuthManager와 SessionManager는 서로 매우 밀접하게 연관된 작업을 한다. AuthManager는 사용자의 계정 인증을 담당하고 세션을 받아오며, SessionManager는 그 세션을 관리한다.&lt;/p&gt;
&lt;p&gt;하지만 기존 코드에서는 세션 관리를 포함한 대부분의 작업을 &lt;code&gt;AuthManager&lt;/code&gt;가 처리하고, &lt;code&gt;SessionManager&lt;/code&gt;는 세션을 소유하는 역할만 하고 있었다.&lt;/p&gt;
&lt;h3 id="역할-분리"&gt;역할 분리&lt;/h3&gt;
&lt;p&gt;기존에는 &lt;code&gt;AuthManager&lt;/code&gt;에서 세션 갱신, 복원, 저장, 로드 작업을 처리하고 있었고, &lt;code&gt;SessionManager&lt;/code&gt;는 세션의 만료 여부 확인과 세션 객체를 소유하는 역할만 맡고 있었다.&lt;/p&gt;
&lt;div class="code-theme-github"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// AuthManager 인터페이스 프로토콜&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;protocol&lt;/span&gt; &lt;span class="nc"&gt;AuthManager&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AuthManager&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kr"&gt;get&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;authStateSubject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PassthroughSubject&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AuthState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Never&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kr"&gt;get&lt;/span&gt; &lt;span class="kr"&gt;set&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;signInAnonymously&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;async&lt;/span&gt; &lt;span class="kr"&gt;throws&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;restoreSession&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;async&lt;/span&gt; &lt;span class="kr"&gt;throws&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;refreshSession&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;async&lt;/span&gt; &lt;span class="kr"&gt;throws&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;loadTokens&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kr"&gt;throws&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// SessionManager 클래스&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SessionManager&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;shared&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SessionManager&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SupabaseSession&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;isExpired&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;session&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 세션 만료를 파악할 때는 30초의 여유시간을 줍니다.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeIntervalSince1970&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TimeInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expiresAt&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;그래서 세션 관리는 &lt;code&gt;SessionManager&lt;/code&gt;, 인증은 &lt;code&gt;AuthManager&lt;/code&gt;가 맡도록 기능을 다시 정의했다.&lt;/p&gt;</description></item><item><title>SniffMEET. 세션 갱신과 정보 접근을 SessionManager에 캡슐화하기</title><link>https://kelly-chui.github.io/posts/sniffmeet-dev-log-supabase-session-manager-encapsulation/</link><pubDate>Mon, 10 Feb 2025 00:00:00 +0000</pubDate><guid>https://kelly-chui.github.io/posts/sniffmeet-dev-log-supabase-session-manager-encapsulation/</guid><description>&lt;h2 id="sessionmanager-사용성-개선"&gt;SessionManager 사용성 개선&lt;/h2&gt;
&lt;h3 id="세션을-갱신하는-방법"&gt;세션을 갱신하는 방법&lt;/h3&gt;
&lt;p&gt;Supabase 서버와의 통신이 필요할 때, 우선 세션의 유효성을 검증해야 한다.&lt;/p&gt;
&lt;p&gt;기존 방식에선 직접 AuthManager의 싱글톤 객체에서 세션을 갱신하는 작업을 했지만&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AuthManager와 SessionManager의 역할 명확하게 분리&lt;/li&gt;
&lt;li&gt;AuthManager의 싱글톤 해제&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;와 같은 변화점이 있었고, 기존 AuthManager가 담당하던 세션 갱신, 복원 작업이 SessionManager로 옮겨졌다. SessionManager의 싱글톤 객체에서 세션의 유효성을 체크하는 컴퓨티드 프로퍼티의 부울리언 값을 체크하여 세션을 갱신하는 메소드를 호출하는 방식을 사용했다:&lt;/p&gt;
&lt;div class="code-theme-github"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="n"&gt;SessionManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isExpired&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="n"&gt;await&lt;/span&gt; &lt;span class="n"&gt;SessionManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;refreshSession&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;이 방식은 SessionManager의 싱글톤 객체 호출이 두 번 일어나고, &lt;code&gt;refreshSession&lt;/code&gt;이라는 메소드를 직접 노출해서, SessionManager가 세션을 갱신하는 방법을 직접 노출하는 문제가 있다.&lt;/p&gt;</description></item><item><title>SniffMEET. 세션 저장 메소드를 공개해도 괜찮을까?</title><link>https://kelly-chui.github.io/posts/sniffmeet-dev-log-session-save-api/</link><pubDate>Mon, 10 Feb 2025 00:00:00 +0000</pubDate><guid>https://kelly-chui.github.io/posts/sniffmeet-dev-log-session-save-api/</guid><description>&lt;h2 id="세션-저장하기"&gt;세션 저장하기&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;AuthManager&lt;/code&gt;와 &lt;code&gt;SessionManager&lt;/code&gt;의 역할을 분리하고 나니, 세션을 받아오는 쪽과 저장하는 쪽이 서로 달라지는 문제가 발생했다.&lt;/p&gt;
&lt;p&gt;(AuthManager가 세션도 관리하던)기존 구조에서는 &lt;code&gt;saveSession(for:)&lt;/code&gt;가 외부에 노출되어 있지 않았는데, 역할이 옮겨진 뒤에는 AuthManager에서 받아온 세션을 SessionManager로 전달해야 했다.&lt;/p&gt;
&lt;h3 id="고민한-점"&gt;고민한 점&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;saveSession(for:)&lt;/code&gt;는 사실상 &lt;code&gt;AuthManager&lt;/code&gt;가 세션을 받아온 상황에서만 필요한 메소드다.&lt;/p&gt;
&lt;p&gt;그런데 이 메소드를 공개하면 캡슐화가 약해진다는 생각을 했고, 다시 AuthManager로 돌리면 SessionManager가 맡아야 할 책임을 되돌리는 것이 되었다.&lt;/p&gt;
&lt;p&gt;따라서 몇 가지 방법을 떠올려 봤다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;(최종 선택) &lt;code&gt;saveSession(for:)&lt;/code&gt;를 공개한다&lt;/li&gt;
&lt;li&gt;&lt;code&gt;saveSession(for:)&lt;/code&gt;를 &lt;code&gt;AuthManager&lt;/code&gt;로 다시 옮긴다&lt;/li&gt;
&lt;li&gt;같은 로직을 &lt;code&gt;AuthManager&lt;/code&gt;와 &lt;code&gt;SessionManager&lt;/code&gt;에 각각 작성한다&lt;/li&gt;
&lt;li&gt;세션이 갱신됐다는 이벤트를 따로 전달할 수 있는 통신 레이어를 만든다&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;처음에 가장 괜찮아 보였던 건 세션 갱신 이벤트를 따로 전달하는 방식이었다.&lt;/p&gt;</description></item><item><title>Network. 쿠키, 세션</title><link>https://kelly-chui.github.io/posts/cs-network-cookie-session/</link><pubDate>Sun, 02 Feb 2025 00:00:00 +0000</pubDate><guid>https://kelly-chui.github.io/posts/cs-network-cookie-session/</guid><description>&lt;h2 id="개요"&gt;개요&lt;/h2&gt;
&lt;p&gt;웹 애플리케이션에서 사용자 정보를 저장하고 관리하는 방식, HTTP는 statelessness이기 때문에, 이전 요청에 대한 정보를 기억하지 않는다. 따라서 이 문제를 해결하기 위해 쿠키와 세션이 필요하다.&lt;/p&gt;
&lt;h2 id="쿠키"&gt;쿠키&lt;/h2&gt;
&lt;p&gt;클라이언트에 저장되는 작은 데이터 파일, 서버가 클라이언트에게 데이터를 저장하도록 지시하면, 해당 데이터를 전송하게 됨&lt;/p&gt;
&lt;h3 id="용도"&gt;용도&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;사용자 인증 정보 저장 (로그인 상태 유지)&lt;/li&gt;
&lt;li&gt;웹사이트 설정 저장&lt;/li&gt;
&lt;li&gt;사용자 행동 추적&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="특징"&gt;특징&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;주기적 전송: 사용자가 특정 사이트에 다시 방문하면 브라우저가 자동으로 쿠키를 서버로 전송함&lt;/li&gt;
&lt;li&gt;수명: 유효 기간을 설정할 수 있고, 만료되면 삭제됨, 유효기간이 없으면 브라우저 세션이 종료될 때 삭제&lt;/li&gt;
&lt;li&gt;용량: 한 도메인당 쿠키는 4KB 정도로 제한&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="보안"&gt;보안&lt;/h3&gt;
&lt;p&gt;사용자의 브라우저에 저장되기 때문에 클라이언트에서 조작할 수 있다. 따라서 민감한 데이터는 쿠키에 저장하면 안됨&lt;/p&gt;</description></item><item><title>SniffMEET. 익명 로그인 세션의 생성·복원·갱신 흐름</title><link>https://kelly-chui.github.io/posts/sniffmeet-dev-log-supabase-anonymous-session-lifecycle/</link><pubDate>Thu, 14 Nov 2024 00:00:00 +0000</pubDate><guid>https://kelly-chui.github.io/posts/sniffmeet-dev-log-supabase-anonymous-session-lifecycle/</guid><description>&lt;h2 id="익명-로그인-생성-복원-갱신-로직-구현"&gt;익명 로그인 생성, 복원, 갱신 로직 구현&lt;/h2&gt;
&lt;p&gt;JWT, Refresh Token, 세션의 개념이 아직 명확하게 정리되지 않은 상태에서 익명 계정을 먼저 만들게 됐다. 개념이 명확하지 않아서, 세션 관리에 꽤 애를 먹었는데, 앱 재실행 이후에도 익명 계정을 어떻게 이어서 사용할지부터 정리할 필요가 있었다.&lt;/p&gt;
&lt;p&gt;생성, 복원, 갱신 로직은 서로 밀접하게 연결되어 있어서, 처음 앱을 실행했을 때와 다시 실행했을 때의 흐름을 먼저 구분해야 했다.&lt;/p&gt;
&lt;h3 id="실제-문제-해결-과정"&gt;실제 문제 해결 과정&lt;/h3&gt;
&lt;p&gt;먼저 시점별로 세션 상태를 표로 정리했다.&lt;/p&gt;</description></item></channel></rss>