Swift. 프로토콜 요구사항과 메소드 어트리뷰트

RemoteDBRequestBuildable 프로토콜을 추가한 뒤, 이전에는 나타나지 않던 unused-result 경고가 발생했다. 구체 타입에서는 사라졌던 경고 기존 구현의 request()에는 @discardableResult가 붙어 있었다. 반환값을 사용하지 않아도 경고가 발생하지 않았다. final class SupabaseDBRequestBuilder { @discardableResult func request() async throws -> Data { Data() } } let concrete = SupabaseDBRequestBuilder() try await concrete.request() 이후 Builder를 프로토콜로 추상화했지만 프로토콜 요구사항에는 해당 어트리뷰트를 붙이지 않았다. protocol RemoteDBRequestBuildable { func request() async throws -> Data } extension SupabaseDBRequestBuilder: RemoteDBRequestBuildable {} let abstract: any RemoteDBRequestBuildable = SupabaseDBRequestBuilder() try await abstract.request() 구체 구현에는 여전히 @discardableResult가 있는데도, 프로토콜 타입으로 호출하면 경고가 다시 나타났다. 호출을 검사하는 선언과 실행할 구현 컴파일러는 호출 지점의 정적 타입을 기준으로 사용할 메서드 선언을 해석한다. ...

February 10, 2025

SniffMEET. 세션 저장 메소드를 공개해도 괜찮을까?

세션 저장하기 AuthManager와 SessionManager의 역할을 분리하고 나니, 세션을 받아오는 쪽과 저장하는 쪽이 서로 달라지는 문제가 발생했다. (AuthManager가 세션도 관리하던)기존 구조에서는 saveSession(for:)가 외부에 노출되어 있지 않았는데, 역할이 옮겨진 뒤에는 AuthManager에서 받아온 세션을 SessionManager로 전달해야 했다. 고민한 점 saveSession(for:)는 사실상 AuthManager가 세션을 받아온 상황에서만 필요한 메소드다. 그런데 이 메소드를 공개하면 캡슐화가 약해진다는 생각을 했고, 다시 AuthManager로 돌리면 SessionManager가 맡아야 할 책임을 되돌리는 것이 되었다. 따라서 몇 가지 방법을 떠올려 봤다. (최종 선택) saveSession(for:)를 공개한다 saveSession(for:)를 AuthManager로 다시 옮긴다 같은 로직을 AuthManager와 SessionManager에 각각 작성한다 세션이 갱신됐다는 이벤트를 따로 전달할 수 있는 통신 레이어를 만든다 처음에 가장 괜찮아 보였던 건 세션 갱신 이벤트를 따로 전달하는 방식이었다. ...

February 10, 2025

SniffMEET. 세션 갱신과 정보 접근을 SessionManager에 캡슐화하기

SessionManager 사용성 개선 세션을 갱신하는 방법 Supabase 서버와의 통신이 필요할 때, 우선 세션의 유효성을 검증해야 한다. 기존 방식에선 직접 AuthManager의 싱글톤 객체에서 세션을 갱신하는 작업을 했지만 AuthManager와 SessionManager의 역할 명확하게 분리 AuthManager의 싱글톤 해제 와 같은 변화점이 있었고, 기존 AuthManager가 담당하던 세션 갱신, 복원 작업이 SessionManager로 옮겨졌다. SessionManager의 싱글톤 객체에서 세션의 유효성을 체크하는 컴퓨티드 프로퍼티의 부울리언 값을 체크하여 세션을 갱신하는 메소드를 호출하는 방식을 사용했다: if try SessionManager.shared.isExpired { try await SessionManager.shared.refreshSession() } 이 방식은 SessionManager의 싱글톤 객체 호출이 두 번 일어나고, refreshSession이라는 메소드를 직접 노출해서, SessionManager가 세션을 갱신하는 방법을 직접 노출하는 문제가 있다. ...

February 10, 2025

SniffMEET. Interactor가 인프라를 직접 알아도 될까

Interactor에서 직접 매니저 호출하지 않기 매니저가 변하면 인터랙터를 직접 변경해야한다. 매니저와 인터랙터 간의 의존성을 줄이고, 인터랙터는 “비즈니스 로직”만, 그리고 비즈니스 로직의 구현체인 유즈케이스에서 네트워크 요청이나 데이터 관리와 같은 인프라 로직을 담당하는 것이 좋다고 생각한다. 또한 싱글톤 객체여도 현재 주입하는 방식으로 사용중인데, 인터랙터에서 사용하면 직접 매니저의 싱글톤 객체를 호출해서 사용해야 한다: func sendWalkRequest(message: String, latitude: Double, longtitude: Double, location: String) { Task { do { let myInfo = try loadUserInfoUseCase.execute() let id = try SessionManager.shared.userID.get() ... try await requestWalkUseCase.execute(walkNoti: walkNoti) } catch { ... } } presenter?.didSendWalkRequest() } 위 코드에서 id를 얻기 위해 SessionManager의 싱글톤 객체를 호출해야 한다. 싱글톤 객체 호출 자체는 크게 문제가 되지 않을 수 있지만, 다음과 같이 생각해볼 주제가 있다. ...

February 10, 2025

SniffMEET. AuthManager와 SessionManager의 책임 분리하기

작업 배경 AuthManager와 SessionManager는 서로 매우 밀접하게 연관된 작업을 한다. AuthManager는 사용자의 계정 인증을 담당하고 세션을 받아오며, SessionManager는 그 세션을 관리한다. 하지만 기존 코드에서는 세션 관리를 포함한 대부분의 작업을 AuthManager가 처리하고, SessionManager는 세션을 소유하는 역할만 하고 있었다. 역할 분리 기존에는 AuthManager에서 세션 갱신, 복원, 저장, 로드 작업을 처리하고 있었고, SessionManager는 세션의 만료 여부 확인과 세션 객체를 소유하는 역할만 맡고 있었다. // AuthManager 인터페이스 프로토콜 protocol AuthManager { static var shared: AuthManager { get } var authStateSubject: PassthroughSubject<AuthState, Never> { get set } func signInAnonymously() async throws func restoreSession() async throws func refreshSession() async throws func loadTokens() throws } // SessionManager 클래스 final class SessionManager { static let shared = SessionManager() var session: SupabaseSession? var isExpired: Bool { guard let session else { return true } // 세션 만료를 파악할 때는 30초의 여유시간을 줍니다. return Date(timeIntervalSince1970: TimeInterval(session.expiresAt + 30)) < Date() } private init() {} } 그래서 세션 관리는 SessionManager, 인증은 AuthManager가 맡도록 기능을 다시 정의했다. ...

February 10, 2025

SniffMEET. ISP로 Supabase 요청 빌더 역할 나누기

들어가며 Supabase REST API를 직접 감싸서 사용하다 보니, 유즈케이스 레이어에서 eq., in., gte. 같은 쿼리 문자열을 직접 조립하는 코드가 늘어났다. 처음에는 단순했지만, 요청이 많아질수록 오타 가능성이 커지고 Supabase의 세부 문법이 상위 레이어로 새어 나오는 문제가 있었다. 이번 글은 이 쿼리 구성 책임을 Supabase DB Layer 안으로 옮기기 위해 요청 빌더와 쿼리 파라미터 래퍼를 도입한 과정을 정리한다. 기본 구조 SNMNetwork의 동작을 간단하게 설명하면, SNMRequestConvertible을 채택하는 객체의 정보로 네트워크 요청을 생성하고, 통신한 다음, 응답을 반환하는 방식으로 동작한다. ...

February 6, 2025

Network. NAT(Network Address Translation)

NAT NAT: Network Address Translation 패킷이 라우팅 장치를 통해 전송되는 동안 패킷의 IP 주소 정보를 수정하여 IP 주소를 다른 주소로 매핑하는 방법, 공인 IP와 사설 IP로 나눠서 처리한다. 동작 원리 NAT는 라우터에서 동작하며, 내부 네트워크의 사설 IP 주소를 외부에서 사용할 수 있는 공인 IP 주소로 변환한다. 이를 통해 여러 장치가 하나의 공인 IP 주소를 공유하면서 인터넷에 접속할 수 있다. 동작 방식 출발지 주소 변환 (SNAT: Source NAT) 내부 네트워크의 컴퓨터가 외부에 패킷을 전송할 때 사설 IP주소를 공인 IP주소로 변환 목적지 주소 변환 (DNAT: Destination NAT) 외부에서 내부로 네트워크로 들어오는 패킷의 목적지 IP를 공인 IP에서 내부 네트워크의 사설 IP로 변환 포트 주소 변환 (PAT: Port Address Translation) 여러 내부 IP들이 하나의 공인 IP를 공유하기 때문에, 포트 번호로 이들을 구별한다. 하나의 공인 IP 주소를 여러 장치가 함께 사용할 수 있도록 포트 번호를 함께 변환하여 구분한다. 예를 들어 다음과 같이 서로 다른 내부 장치가 하나의 공인 IP를 사용할 수 있다. ...

February 5, 2025

OS. Stack, Heap 메모리 관리

Date: February 2, 2025 Multi-select: Memory 스택 메모리 LIFO 구조로, 함수 호출 시 생성되는 스택 프레임(로컬 변수, 파라미터, 반환 주소 등)을 저장한다. 함수가 호출 될 때 마다 새로운 스택 프레임이 쌓이고, 함수가 종료되면 스택 프레임 제거된다. 특징 빠른 메모리 할당/해제: 스택은 메모리가 자동으로 관리되어서 함수가 끝나면 알아서 메모리가 반환됨 고정 크기: 스택 메모리는 크기가 제한적이라서 너무 많은 데이터를 저장하면 스택 오버플로우 위험이 있음 호출 상태 저장: 함수의 매개변수와 지역 값 일부가 스택 프레임에 저장될 수 있음. 실제 저장 위치는 ABI와 컴파일러 최적화에 따라 레지스터나 다른 영역이 될 수도 있음 스레드별 스택: 각 스레드는 별도의 호출 스택을 가지지만, 이것만으로 함수나 데이터 전체가 thread-safe해지는 것은 아님. 스택에 있는 참조가 공유 객체를 가리킬 수도 있음 스택 오버플로우 프로그램이 스택 메모리를 초과할 때 발생하는 오류, 함수 호출 시마다 생성되는 스택 프레임이 쌓이면서 스택 공간을 사용하게 되고, 이 제한된 스택 공간을 초과해서 스택 프레임이 쌓일 경우 발생한다. ...

February 2, 2025

Network. 쿠키, 세션

개요 웹 애플리케이션에서 사용자 정보를 저장하고 관리하는 방식, HTTP는 statelessness이기 때문에, 이전 요청에 대한 정보를 기억하지 않는다. 따라서 이 문제를 해결하기 위해 쿠키와 세션이 필요하다. 쿠키 클라이언트에 저장되는 작은 데이터 파일, 서버가 클라이언트에게 데이터를 저장하도록 지시하면, 해당 데이터를 전송하게 됨 용도 사용자 인증 정보 저장 (로그인 상태 유지) 웹사이트 설정 저장 사용자 행동 추적 특징 주기적 전송: 사용자가 특정 사이트에 다시 방문하면 브라우저가 자동으로 쿠키를 서버로 전송함 수명: 유효 기간을 설정할 수 있고, 만료되면 삭제됨, 유효기간이 없으면 브라우저 세션이 종료될 때 삭제 용량: 한 도메인당 쿠키는 4KB 정도로 제한 보안 사용자의 브라우저에 저장되기 때문에 클라이언트에서 조작할 수 있다. 따라서 민감한 데이터는 쿠키에 저장하면 안됨 ...

February 2, 2025

Network. 프록시

프록시 클라이언트와 서버 사이에 중간 역할을 하는 시스템, 클라이언트의 요청을 대신 처리하고 서버로부터 받은 응답을 클라이언트에 전달하는 방식으로 작동 프록시는 클라이언트와 서버 사이에서 요청과 응답을 대신 전달하는 중개 서버이다. 특징 익명성 제공 클라이언트의 실제 IP 주소를 숨기고, 프록시 서버의 IP 주소 사용 캐싱 자주 요청되는 데이터를 미리 저장해두고, 클라이언트의 요청이 있을 떄 빠르게 응답 보안 프록시 서버는 클라이언트와 서버 사이에 존재, 보안 및 필터링 기능 제공 트래픽 제어 리소스 보호를 위해 트래픽 필터링 종류 포워드 프록시 클라이언트가 외부 서버에 접근할 때 중개 역할을 하는 프록시 ...

February 1, 2025