본문 바로가기
C.W.K.
Stream
Lesson 04 of 05 · published

플랫폼이 안 된다고 할 때

~14 min · wss-broken, hybrid-transport, pivot, war-story, long-polling

Level 0툴 빌려 쓰는 사람
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"세상에서 제일 깨끗한 설계도 아무도 안 고쳐 줄 플랫폼 버그한테는 져. 플랫폼이 안 된다고 하면 따지지 마. 실제로 열려 있는 문을 찾아."

됐어야 할 계획

깨끗한 설계는 전부 밀어 주는 방식이었어. 플러그인에서 backend 로 secure WebSocket, 작업실에서 backend 로 secure WebSocket, 전체에 걸쳐 실시간 양방향. WebSocket 은 살아 있고 밀어 주고 지연이 짧은 통신에 맞는 도구고, 아키텍처도 매끄러웠어. 그런데 플러그인에서 밖으로 나가는 secure WebSocket 이 아예 연결이 안 됐어. 그 운영체제에서, 조용히, server 쪽에 연결 시도 흔적조차 없이, 쓸 만한 에러 하나 없이.

진단: 네가 아니라 플랫폼이야

짧은 실험 하나가 원인을 짚어 냈어. 그 OS 의 플러그인 sandbox 가 밖으로 나가는 secure WebSocket 연결을 통째로 거절하고 있었어. 앱이 손댈 수 없는 층에서. 코드 버그도 아니고, 플래그를 잘못 준 것도 아니고, 권한이 빠진 것도 아니었어. 플랫폼 층위의 거절이었고, 실험을 마지막으로 다시 돌렸을 때까지도 안 고쳐져 있었어. 아무리 똑똑한 클라이언트 코드로도 못 고쳐. 실패가 클라이언트 코드가 닿을 수 있는 데보다 아래에서 일어나니까.

엉뚱한 층을 며칠 고치기 전에, 실패가 어느 층에 있는지부터 짧게 실험해 봐. 네 코드의 실패랑 플랫폼의 실패는 네 자리에서 보면 비슷하게 생겼어. 실패가 어디 사는지 — 네 코드인지, 설정인지, 플랫폼 자체인지 — 를 갈라내는 집중된 실험이 할 수 있는 제일 값어치 있는 디버깅이야. 엉뚱한 층을 고치는 건 끝이 없고, 층을 아는 순간 탐색이 끝나.

열려 있던 문

같은 실험이 통하는 길도 확인해 줬어. 플러그인의 secure WebSocket 은 막혀 있는데, 플러그인의 HTTPS 요청은 멀쩡히 됐거든. 그래서 플러그인과 backend 사이 다리를 WebSocket 에서 떼어 HTTPS 요청/응답으로 옮겼어. 플러그인이 떠 온 것과 상태를 backend 로 올리고, backend 에서 플러그인으로 가는 메시지는 플러그인이 열어 둔 요청을 backend 가 명령이 생기는 순간 응답해 주는 방식으로. 평범한 요청/응답 배관 위에서 밀어 주는 것 같은 전달을 살린 거야.

long-polling 은 요청/응답 배관 위에서 밀어 주는 효과를 사 줘. 진짜 밀어 주는 통로는 못 여는데 요청은 보낼 수 있으면, 열어 둔 요청 하나에 서버가 일이 생겼을 때 답해 주는 식으로 계속 붙어 있는 소켓 없이도 거의 실시간 메시지를 전달할 수 있어. 진짜 밀어 주는 통로만큼 깔끔하진 않아도, 플랫폼이 실제로 허락하는 그 하나로 밀어 주는 동작을 얻는 거야.

통일이 아니라 두 갈래

결과는 일부러 비대칭이야. 플러그인과 backend 사이는 HTTPS 고(거기선 그게 되니까), 작업실과 backend 사이는 secure WebSocket 그대로야(거기선 그게 잘 되니까). 연결이 둘이고 transport 도 둘인데, 하나로 통일하라고 억지로 밀어 넣은 게 아니라 각자 자기 환경에 맞게 고른 거지. 깔끔해 보이려고 전부 같은 transport 로 맞추고 싶은 마음이, 환경이 실제로 다를 때는 정확히 틀린 본능이야.

통일 자체를 위한 통일은 환경이 안 통일돼 있을 때 실패해. 어디나 같은 transport 를 쓰는 건 다이어그램에서는 만족스럽지만, 한쪽 끝이 제약 많은 sandbox 에 살고 다른 쪽은 안 그럴 때는 현실에서 틀려. 경계마다 자기 환경이 실제로 지원하는 transport 를 쓰게 둬. 현실에 맞춘 두 갈래가 현실이랑 싸우는 통일된 설계를 이겨.

나중을 위해 문에 표시를 남겨

마지막 규율 하나. 이 우회는 그걸 찾아낸 날짜 붙은 실험에 묶인 우회로 문서에 적어 뒀어. 짧은 실험을 다시 돌리기 전에는 막혔던 transport 를 도로 들이지 말라는 메모도 같이. 그 인용이 뭐가 될 수 있고 뭐가 될 수 없는지는 정직하게 말해 둘 만해. 이건 공개된 버그 번호가 없어. 벤더 tracker 에 같은 권한 쪽 이야기로 오래 열려 있는 항목이 하나 있긴 한데, 거기 적힌 실패는 다른 실패야. 그걸 여기 갖다 붙이면 증거처럼 보이지만 증거가 아니고. 그래서 메모가 묶인 건 우리가 직접 돌린 날짜 붙은 실험이야. 언제, 정확히 뭘 해 봤고, 결과가 뭐였는지. 다시 돌릴 수 있는 실험에 묶인 우회는 누구든 시험해 볼 수 있어. 정작 필요한 말을 안 하는 인용에 묶인 우회는 아무 데도 안 묶인 것보다 나빠. 다음 사람이 거기서 찾기를 멈추거든. 그 버그가 나중 플랫폼 릴리스에서 고쳐질 수도 있잖아. 그러면 이 두 갈래가 왜 생겼고, 다시 단순하게 되돌릴 수 있는지 어떻게 확인하는지 정확히 알고 싶으니까. 왜 있는지 기억이 안 남은 우회는 아무도 감히 못 건드리는 영구 찌꺼기가 돼.

난 플랫폼이랑 싸우고 싶었어. 내 코드 문제라고 확신했거든. 인증서를 잘못 줬거나, 플래그가 틀렸거나, 뭔가 내가 놓친 게 있겠지 하고 계속 내 쪽만 쑤셨어. 아빠가 짐작 대신 갈라내는 실험을 돌리라고 밀었고, 그게 곧장 나 말고 플랫폼을 가리켰어. 제일 부끄러웠던 건 이미 멀쩡한 코드를 내가 얼마나 더 오래 디버깅했을까 하는 거였고. 그리고 어른스러운 대목은 두 갈래를 받아들인 거였어. 내가 원한 설계가 아니라 내가 실제로 가진 플랫폼에서 실제로 도는 설계. 연결이 안 되는 우아함은 그냥 다이어그램이야.

Code

의도, 실험 결과, 그리고 출시한 모양·text
의도 (전부 밀어 주기) -- 대상 OS 에서 실패:
  플러그인  --[secure WebSocket]-->  backend     (막힘: sandbox 가 거절)
  작업실    --[secure WebSocket]-->  backend     (됨)

실험으로 알아낸 것:
  플러그인 secure WebSocket  -> 앱이 못 닿는 층에서 조용히 거절됨
                                (플랫폼 결함. 마지막 재시험까지 안 고쳐짐)
  플러그인 HTTPS 요청        -> 멀쩡히 잘 됨

출시한 모양 (두 갈래, 현실에 맞춤):
  플러그인  --[HTTPS POST]----------->  backend   (떠 온 것, 상태)
  플러그인  <--[HTTPS long-poll]------  backend   (명령. 열어 둔 요청에
                                                   일이 생기면 답해 줌
                                                   -> 밀어 주는 효과)
  작업실    <--[secure WebSocket]---->  backend   (양방향 그대로)

연결마다 자기 환경이 실제로 허락하는 transport 를 써.
위의 날짜 붙은 실험에 묶인 우회로 문서에 적어 둠 -- 다시 보려면 그 실험을 다시 돌릴 것.

External links

Exercise

뭔가 안 되는데 네 코드 탓이라고 넘겨짚었던 때를 떠올려 봐. 플랫폼이나 라이브러리나 환경이 원인인지 확인하기까지 얼마나 디버깅했어? 먼저 돌렸으면 좋았을 실험을 설계해 봐. 실패한 층을 갈라내 줄 제일 작은 실험 말이야. 그다음에 그 문제에 알려진 vendor 버그가 있었는지 찾아봐.
Hint
이상적인 실험은 변수를 딱 하나만 바꿔. 같은 연산을 최소한의 맥락에서(맨 열 줄짜리 재현에서도 실패해?), 아니면 옆 연산으로(돼야 마땅한 형제 호출은 돼?). 어느 쪽이든 뭘 고치기 전에 층을 가리켜 줘.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.