현재 사용되고 있는 네트워크 프로토콜의 수는, 커스텀 프로토콜을 제외하더라도 약 300~500개 수준으로 알려져 있습니다.이를 웹 영역으로 한정하더라도, 브라우저와 서버 간 통신을 중심으로 약 15~20개의 프로토콜이 실제로 사용되고 있습니다.흥미로운 점은, 이 중에서도 웹의 핵심 동작을 책임지는 프로토콜은 8~10개 내외에 불과하다는 사실입니다.여기서 자연스럽게 하나의 의문이 생깁니다. 왜 이렇게까지 많은 종류의 프로토콜이 필요했을까요?단순히 신호를 보내고, 받고, 응답하는 방식만으로는 충분하지 않았을까요?조금 더 근본적인 질문도 던져볼 수 있습니다. 프로토콜은 정말 "필요하니까" 즉흥적으로 만들어진 결과물일까요?아니면, 각각의 프로토콜은 서로 다른 문제를 해결하기 위한 명확한 선택의 결과일까요?이 질..
REST(Representational State Transfer)는 HTTP를 사용하여 통신하는 API를 구축하기 위한 아키텍처 스타일입니다. RESTful API로 간주되려면 다음 여섯 가지 핵심 제약 조건을 준수해야 한다고 합니다. 1. 클라이언트-서버방식 2. 무상태 3. 균일한 인터페이스 4. 캐시 가능5. 계층형 시스템 6. 코드 온 디멘드 6번째는 선택사항입니다. 이들이 REST와 어떤 관련이 있는지 확인해봅시다. 클라이언트와 서버 방식이 원칙은 클라이언트와 서버를 물리적으로 나누라는 의미가 아니라, 책임을 명확히 분리하라는 뜻이다.이렇게 구분하는 이유는 각 컴포넌트가 서로의 구현 세부 사항에 의존하지 않도록 하기 위함이다.클라이언트는 클라이언트 상태를 책임진다.여기서 말하는 상태란 화면 전..
스프링으로 개발하다 보면, "요청이 들어왔을 때 공통적으로 처리해야 하는 로직"을 어디에 둘지 고민하게 됩니다. 예를 들어 인증, 로깅, MDC 세팅, 트래픽 제어, 요청 검증 같은 것들입니다. 스프링에는 이런 공통 처리를 위한 장치로 Filter, Interceptor, AOP라는 세 가지 선택지가 존재합니다.문제는, 이 셋이 할 수 있는 일의 범위가 상당히 겹친다는 점입니다. 실제로 많은 경우, 필터로 구현한 로직을 인터셉터로 옮겨도 되고, 심지어 AOP로 구현해도 기능적으로는 큰 문제가 없어 보이기도 합니다. 그래서 "아무거나 써도 되는 거 아닌가?"라는 생각이 들기 쉽습니다.하지만 이 세 가지는 동작 시점과 책임 레벨이 완전히 다릅니다. 이 차이를 무시한 채 사용하면, 당장은 동작하더라도 시스템이..
현재 구독 서비스를 개발하고 있습니다.왜 뜬금없이 구독 서비스를 이야기하느냐 하면, 개발을 하다 보니 옵저버 패턴과 구조적으로 비슷하다는 느낌을 받았기 때문입니다.옵저버 패턴은 어떤 주체의 상태 변화를 관찰자들이 지켜보고 있다가, 변화가 발생하면 그에 맞춰 반응하는 구조를 가집니다.구독 서비스 역시 특정 대상에 대해 사용자가 구독 여부를 맺고, 대상의 상태나 이벤트가 발생했을 때 그 결과를 사용자에게 전달합니다. 이 흐름만 놓고 보면 두 개념은 꽤 닮아 있습니다.하지만 여기서 조심해야 할 부분이 있습니다.이 유사성이 단순한 구조적 유사성인지, 아니면 설계 패턴 관점에서 옵저버 패턴이라고 불러도 되는지는 쉽게 단정할 수 없기 때문입니다. 특히 실무에서 사용하는 구독 모델을 그대로 옵저버 패턴이라고 정의해버..
PASETO는 쿠키, 세션, JWT와 같은 기존 인증 방식의 운영 보안 상 한계를 보완하기 위해 등장한 토큰 기반 인증 방식입니다.특히 JWT의 알고리즘 선택 자유도가 오히려 취약점으로 작용해온 문제를 해결하기 위해, PASETO는 안전한 암호화 알고리즘만을 강제하는 설계를 채택했습니다. 이 글에서는 PASETO가 등장하게 된 배경과 기존 인증 방식들과의 구조적 차이를 정리합니다.왜 등장했을까?PASETO는 JWT의 암호학적 취약점 자체를 해결하기 위해 등장했다기보다는,JWT가 가진 과도한 유연성과 그로 인한 오용 가능성을 줄이기 위해 등장한 토큰 인증 방식입니다.JWT는 서버 상태를 최소화하고 확장성을 높인다는 장점이 있지만, 토큰이 서명만 되어 있을 뿐 암호화되지 않아(payload가 Base64로 ..
캐시는 보통 읽기 성능을 올리기 위해 사용한다고 알고 있습니다.하지만 그렇다고 해서 모든 곳에 캐싱을 적용할 수는 없습니다. 캐시는 성능을 얻는 대신, 정합성을 어느 정도 포기해야 하는 구조이기 때문에 무작정 사용하는 건 오히려 리스크가 될 수 있습니다.또 하나 짚고 넘어가야 할 점은, 캐싱만으로 모든 읽기 문제를 해결하려는 접근은 좋은 전략이 아니라는 점입니다. 캐시는 어디까지나 보조 수단이지, 데이터의 진짜 기준(Source of Truth)을 대체하지는 않습니다.그렇다면 캐시가 없거나, 캐시 미스가 발생한 경우 데이터는 어디에서 가져와야 할까요? 흔히 RDB에서 가져온다고 생각하기 쉽지만, 이것도 여러 선택지 중 하나일 뿐입니다. 실제로는 데이터의 성격에 따라 검색 엔진, 별도의 조회용 스토어, 외..
개발을 하다 보면 문법적으로는 성공하는데, 실제로는 정상적으로 동작하지 않는 코드들이 분명히 생깁니다. 이런 문제들은 보통 테스트를 통해 잡게 되는데, 테스트는 결국 코드를 직접 실행시키는 과정이다 보니 비용이 들고, 자주 돌리기에는 부담이 되는 것도 사실입니다.그래서 모든 문제를 항상 테스트까지 가져가기보다는, 코드만 보고도 "이건 위험하다"라고 판단할 수 있는 것들은 최대한 앞단에서 걸러내는 게 더 효율적입니다. 이 역할을 해주는 게 Lint 같은 정적 분석 도구입니다.Lint는 코드를 실행하지 않고, 코드 자체를 분석해서 잠재적인 버그 가능성이나 실수하기 쉬운 코드 패턴들을 찾아내고, 개발자에게 미리 수정하라는 신호를 줍니다. 다만 정적 분석이다 보니, 실제로 값이 어떻게 들어오는지에 따라 달라지는..
MDC는 ThreadLocal에 로그 컨텍스트를 저장해두고, logframwork가 자동으로 꺼내 쓰게 만드는 장치라고 합니다. 위의 메시지로 그려보면 위와 같은 그림이 그려집니다. 그렇다면 우리는 어떤것을 학습해야 할까요?1. Thread Local이란 무엇인가?2. 무엇을 보고 logFramwork는 ThreadLocal에서 꺼내게 만드는가?요런것들이 학습되어야 하지 않나 생각이 듭니다. MDC가 비동기에서 어떻게 동작하는지 실제로 설정은 어떻게 하는지 등등을 학습하면 될거 같습니다. Thread Local이란 무엇이고 어떻게 저장하는가?Thread Local은 쓰레드마다 가지고있는 저장소라고 합니다. 그렇다면, 각각 쓰레드는 어떻게 ThreadLocal에 저장할까요?ThreadLocal t = ne..
지금까지 두 가지 생성 패턴을 학습했습니다. 팩토리 패턴과 빌더 패턴입니다. 팩토리 패턴은 객체 생성 과정에서어떤 구현체를 생성할 것인가에 대한 결정을 호출부에서 분리하는 데 초점이 맞춰진 패턴입니다.즉, 생성의 선택 책임을 한 곳으로 모아 코드의 결합도를 낮추는 것이 핵심입니다. 반면 빌더 패턴은 객체를 어떻게 구성해서 생성할 것인가에 집중합니다.생성자 파라미터 폭발 문제를 해결하고, 복잡한 객체를 단계적으로 조립함으로써 가독성과 안정성을 확보하는 것이 목적입니다. 그렇다면, 퍼사드 패턴은 어떤 패턴일까요? 뭐랄까 이전의 패턴들과 다르게 용어부터 심상치 않습니다. 퍼사드는 외관이라는 뜻을 가지고 있다고 합니다. 그렇다면, 퍼사드 패턴은 겉보기에 괜찮아 보이게 만드는 패턴일까요?왜 탄생하게 되었을까?가장..
서비스 규모가 커지면서 쓰기(write) 작업은 점점 많아졌고, 자연스럽게 각 서비스는 자신에게 맞는 방식으로 데이터를 저장하기 시작했습니다. 어떤 서비스는 데이터베이스에 직접 쓰고, 어떤 서비스는 메시지 큐를 거쳐 비동기로 처리했습니다.문제는 장애가 발생했을 때 드러났습니다. 쓰기 경로가 서비스마다 달라지면서 "이 쓰기가 실제로 처리되었는지","어디까지 반영된 상태인지"를 판단할 수 없게 된 것입니다. 즉, 시스템 전체의 쓰기 상태를 하나의 기준으로 확인할 수 없게 되었습니다.넷플릭스는 이 문제를 개별 서비스의 로직을 고치는 방식이 아니라, 쓰기 자체의 흐름을 통제하는 방식으로 해결했습니다.그 선택이 바로 WAL(Write-Ahead Logging)이었습니다. 이 글에서는 넷플릭스가 WAL을 통해 분산..
개발을 하다 보면 멀티모듈이라는 말을 들어보았었다. 단순히 모듈을 여러 개 사용하는 거라고 생각했다. 하지만 사용할 때마다 빌드가 되지 않는 상황이 발생하였다. 결과적으로는 "되긴 되게" 만들어 놓고 넘어갔다. 즉, 해결은 했지만 이해하지는 못한 상태였다. 멀티모듈을 사용하게 되면 중복제거를 하면 좋다는 정도만 알았고, 반대로 멀티 모듈을 도입하면서 발생하는 빌드 복잡도 증가, 의존성 관리 비용, 실행 구조의 제약과 같은 손해(트레이드오프)에 대해서는 전혀 고려하지 않았던거 같다. 지금 돌이켜보면, 문제의 원인은 멀티 모듈 자체가 아니라구조를 이해하지 않은 상태에서 구조를 도입하려 했다는 점에 있었다.멀티모듈을 왜 사용하는가?멀티모듈을 왜 사용할까요? 사용하지 않으면 다음과 같이 처리 할 수 있습니다. ..