hayou
log12min read2

[후기] Slash24에 다녀왔습니다 (feat. Toss)

[후기] Slash24에 다녀왔습니다 (feat. Toss)

[후기] Slash24에 다녀왔습니다 (feat. Toss)

0. 들어가며

우아콘23-인프런24-SLASH24... 과연 당근 테크 밋업까지 성공할 것인가!? (FECONF는 광탈ㅠ)

프론트엔드 채널에 정말 공을 들이는 토스의 컨퍼런스이기에 기대감x100이었다.

하지만 정작 프론트엔드 세션은 4개가 전부였고, 그마저도 React-Native를 제외하면 3개 뿐이었다.

뿐만 아니라 세션들은 20여 분의 짧은 시간만 편성되어 연사분들의 이야기를 100% 다 담기에는 부족한 감이 있었다.

그리고 구미가 당기는 스페셜 세션은 전체 추첨제로 진행된... 여러모로 조금의 아쉬움이 남는 컨퍼런스였다. (절대 스페셜 세션 추첨 떨어져서 그런거 아님)

그럼에도 불구하고, 각 세션마다 새로운 인사이트를 얻을 수 있는 소중한 시간이었다.

그 중 가장 인상 깊었던 강의 내용들을 소개하고자 한다.

1. 토스가 오프라인 결제를 빠르고 안정적으로 혁신하는 방법

하루에도 수십번 배포가 가능했던 이유

오프라인 결제를 위한 POS 앱은 Electron 기반으로 만들어졌는데, 전체 앱을 배포할 때마다 리소스를 많이 요구하는 문제가 있었다.

이를 해결하기 위해 토스는 앱에서 웹뷰를 분리하고, 웹뷰 번들 개념을 활용한 마이크로 프론트엔드 아키텍처를 적용했다.

또한, 크로스 플랫폼 이슈를 해결하기 위해 다음과 같은 구조를 도입해 다양한 운영체제에서 빠른 앱 개발이 가능해졌다.

안정적으로 수십만건의 결제 만들어내기

기기간 연결 안정성(유선 연결): 토스는 유선 연결에서 발생할 수 있는 메시지 누락 문제를 해결하기 위해 hand-shake 기능과 CRC 체크를 도입했다. 이를 위해 프로토콜에 ATA, ACK, NAK 패킷을 추가하고, 타임아웃과 예외 처리 기능을 통해 안정성을 강화했다.

기기간 연결 안정성(무선 연결): 무선 연결의 경우 결제 단말의 IP가 동적으로 변할 때 POS와의 연결이 해제되는 문제가 발생할 수 있었다. 이를 해결하기 위해 결제 단말이 연결이 해제될 때 브로드캐스트를 전송하도록 하고, 이를 수신한 POS가 새로운 IP로 재연결을 요청하는 DNS-SD 방식을 사용했다.

결제 로직의 안정성: 결제 로직의 경우 복잡하고 분기가 많아 버그 발생 위험이 높았다. 이를 해결하기 위해 코드를 시점에 따른 관심사별로 분리하고, 결제의 라이프사이클을 정의해 Hook을 따로 분리했다. 추가적으로 플러그인 구조를 도입해 부가 기능과 핵심 로직을 분리함으로써, 배포 안정성까지 확보할 수 있었다.

느낀점: 최고다! CS짱!

이처럼 기초 CS 지식을 활용해 실제 문제를 해결하는 과정을 보며, CS 지식의 중요성을 다시금 실감할 수 있었다.

2. N개의 탭, 단 하나의 웹소켓: SharedWorker

우리 회사는 HTTP 통신이 아닌 WebSocket 기반으로 통신을 수행하고 있다. 이로 인해 다수의 소켓 연결 요청이 서버로 전송되며, 특히 PC의 경우 브라우저 탭마다 별도의 WebSocket 연결을 맺는 문제가 간과되었다.

이번 세션을 통해 이러한 문제를 인식하게 되었고, 강의 내용을 바탕으로 이를 해결해 나가야겠다는 다짐을 하게 되었다.

PC 버전과 함께 발생한 서버 부하 이슈

지난 7월, 토스 증권은 PC 버전을 출시했다.

기존 모바일 앱 버전에서는 1:1 방식으로 단말기와 서버 간 연결을 맺어 한 사용자가 유지할 수 있는 연결 수에 자연스러운 제한이 있었다.

그러나 PC 버전에서는 브라우저 탭마다 서로 다른 스레드를 사용해 N:1 연결을 맺게 되어 서버 부하가 급격히 증가할 수 있는 우려가 발생했다.

이 문제를 해결하기 위해 토스증권팀은 다양한 접근 방식을 검토했다.

대표적으로 Focus & Blur 방식과 Visibility Change 방식을 고려해, 사용되지 않는 WebSocket 연결을 끊는 방식이 제안되었으나, 사용자 경험에 부정적인 영향을 미친다는 이유로 채택되지 않았다.

결국 여러 탭의 WebSocket 연결을 외부로 분리해 단일 WebSocket으로 관리하는 방안이 필요하다는 결론에 도달했고, 이를 위해 WebWorker를 활용하는 방법을 검토하게 되었다.

웹 워커(Web worker)는 스크립트 연산을 웹 어플리케이션의 주 실행 스레드와 분리된 별도의 백그라운드 스레드에서 실행할 수 있는 기술이다. (출처: https://developer.mozilla.org/ko/docs/Web/API/Web_Workers_API)

웹 워커의 경우 Dedicated Worker와 Shared Worker 두 종류가 있다.

Dedicated Worker의 경우 Worker를 만든 탭에서만 접근 가능하다는 한계가 있어 사용하지 않았다. 대신, 여러 탭에서 공유 가능한 Shared Worker를 활용해 문제를 해결하기로 했다.

const worker = new SharedWorker(”/worker.js”);

각각의 탭 생성한 worker는 SharedWorker에서 MessageChannel을 활용해 메시지를 받아 온다.

worker.port.postMessage, worker.port.addEventListener

이를 바탕으로 WebSocket을 SharedWorker에서 생성해 서버와 통신하고, 통신 결과를 각각의 탭(브라우저)으로 보내 이후 로직을 처리한다.

이를 실제로 구현하기 위해 여러 난관이 있었는데, 번들링 문제와 메모리 누수 이슈가 있었다.

번들링 이슈

SharedWorker가 공유되기 위해서는 두 가지 기준을 충족시켜야 한다.

첫째, origin이 같아야 함.

둘째, 불러온 JS 파일이 같아야 함.

이때 번들러가 불러오는 파일명을 바꾸기 때문에 JS 파일명이 달라질 경우 같은 Web Worker를 불러오지 못하는 이슈가 있었다.

이를 해결하기 위해 Webpack의 기능을 활용해 다음과 같이 고정된 URL로 변경해 JS 파일을 불러왔다.

const worker = new SharedWorker(new URL(”/worker.js”, import.meta);

메모리 누수 이슈

Message port(이하 port)에서는 닫힐때의 이벤트를 따로 제공하지 않기 때문에 탭이 닫혔을 때 callback을 호출할 수 없다.

이를 해결하기 위해 port의 특성을 이용했는데, 바로 탭이 닫히면 port가 가비지컬렉션 되는 특성이다.

그러나 실제로는 SharedWorker에서 port의 가비지컬렉션이 일어나지 않는데, 해당 객체들을 Array에 담아서 보관하고 있기 때문이다.

결국 이를 해결하기 위해 WeakRef를 활용했다.

WeakRef로 port를 감싸 탭이 닫혀 port가 가비지컬렉션 될 경우, weakRef가 undefined가 되고 이를 디스패치 해 처리 로직을 작성하면 되는 것이다.

const weakPort = new WeakRef(port);

portList.push(weakPort);

if(!weakPort.deref()) {
  // 이후 처리 로직
}

3. 마치며

이외에도 여러 세션을 들었으나...

이해도가 부족해 이 글에 담을 수 없었던 세션도 있었고(yarn plugin... 얼른 공부해서 내 지식으로 만들어야지), 다른 직군의 이야기이다 보니 흥미도가 떨어지는 세션들도 있었다.

그럼에도 불구하고, 이번 컨퍼런스는 토스가 어떻게 기술적으로 문제를 해결하고 발전해 나가는지 직접 엿볼 수 있는 기회였다.

특히 CS 기초 지식이 실무에서 어떻게 활용되는지를 생생하게 경험하며, 앞으로의 개발 방향성을 고민하게 하는 계기가 되었다.

이번 경험을 바탕으로 나도 이러한 기술적 도전과 해결 과정을 실무에 적용해보고, 보다 견고한 시스템을 구축하기 위해 끊임없이 학습해야겠다고 느꼈다.

토스의 혁신적인 접근 방식과 실질적인 해결책들은 우리 팀에도 큰 영감을 줄 수 있는 부분이었다고 생각한다.

앞으로도 이러한 기술적 교류의 기회를 놓치지 않고, 더 나은 개발자가 되기 위해 노력해야겠다.

+진행적으로 아쉬웠던 점)

  • 기업 부스가 너무 적었다.
  • 프론트엔드 세션이 너무 적었다.
  • 스페셜 세션 추첨제...
  • 소프트스킬에 관한 세션이 더 많았으면 좋았을 것 같다.
share

comments

loading…