Skip to content

Latest commit

 

History

History
218 lines (143 loc) · 15 KB

research-method.md

File metadata and controls

218 lines (143 loc) · 15 KB

설계 근거는 어디서 나왔나 — 조사 방법과 결론

이 프로젝트의 스키마와 아키텍처는 상상해서 만든 게 아니라 조사해서 역추론했습니다. 이 문서는 그 방법과 결론입니다.

익명 처리: 조사 대상은 실제로 운영 중인 서비스라 플랫폼 A·B·C 로 표기하고, 추적 식별자·서버 주소 같은 특정 값은 싣지 않습니다. 조사는 비로그인 공개 페이지·공개 응답 헤더·공개 약관·공개 API 범위에서만 했고, 로그인·결제·자동 수집·CAPTCHA 우회는 하지 않았습니다.

성격
플랫폼 A 글로벌 웹툰 플랫폼. 10개국 11개 언어 서비스. PHP 모놀리스
플랫폼 B 대형 웹툰 플랫폼. SPA + 공개 JSON API. 한국판·글로벌판 별도
플랫폼 C 웹툰 플랫폼 (보조 확인용)

1. 왜 조사부터 했나

처음엔 채용 공고의 스킬 태그(PHP MySQL Cookie CORS Redirect GA Meta Pixel)만 보고 바로 설계를 시작하려 했습니다. 그런데 그렇게 하면 "광고 태그를 붙여본 사람" 까지밖에 못 갑니다.

광고 연동 코드는 어딘가 위에 올라앉습니다. 그 도메인(다국어·결제·회원·콘텐츠)을 모르면 전환이 무엇인지, 전환에 어떤 값이 실려야 하는지 정할 수 없습니다.

그리고 이 도메인은 공개 자료만으로 상당 부분 역추론이 가능합니다.

자료 여기서 나오는 것
개인정보처리방침 회원 테이블의 실제 수집 항목, 보유기간(라이프사이클), 위탁·제3자 제공 목록 = 붙어 있는 외부 시스템 명단
이용약관·환불정책 재화 만료 규칙, 결제 상태 머신
공개 응답 헤더·쿠키 프레임워크, 세션 방식, 어트리뷰션 구조, DB 구성
공개 JSON API 실제 운영 스키마 (필드명·타입·플래그)
경쟁 플랫폼 교집합 웹툰 도메인의 필수 개념

2. 방법

단계 도구 얻은 것
공개 페이지 구조 브라우저 자동화 (접근성 트리·링크 수집) URL 라우팅 규칙, 목록/랭킹 축, 상태 뱃지
태그 실측 페이지 컨텍스트에서 dataLayer/gtag/fbq 존재 확인, 네트워크 요청 관찰 붙어 있는 광고·분석 매체 종류와 개수
응답 헤더 curl -I 웹 서버 종류, 세션 쿠키 방식, DB 구성 단서
약관·방침 본문 텍스트 추출 후 항목 구조만 정리 수집 항목, 보유기간, 위탁사 분류, 재화 규칙
공개 API curl 실제 운영 스키마
표준 확인 웹 검색 + 1차 출처 규격 최신 버전 (아래 5장)

차단됐을 때의 대체 경로: 브라우저 자동화 → 실제 브라우저 → curl/문서 fetch → 2차 자료 + [미확인] 표시. 실제로 플랫폼 B 한국판은 앞의 두 경로가 막혀 curl로 내려갔는데, 렌더링된 화면보다 공개 API가 오히려 정확했습니다.

표기 규칙: 모든 관측에 [확인] / [추정] / [미확인] 을 붙였습니다. 추론을 관측으로 포장하지 않기 위해서입니다.


3. 조사가 바꾼 설계 결정

3-1. 서브도메인 3개 → 등록 도메인 2개 → 다시 1개 ★ 두 번 뒤집힌 결정

초안은 lp. / api. / app.example.com 서브도메인 3개로 "다른 오리진"을 만들려 했습니다.

틀렸습니다. same-site 판정은 오리진이 아니라 등록 도메인(eTLD+1) 기준이라 셋 다 same-site입니다. SameSite=None도, 서드파티 쿠키 차단도 재현되지 않습니다 → ADR-002

그리고 그 결론도 틀렸습니다. 위 문단에서 저는 "CORS는 발생하지만" 이라고 써 놓고, 잃는 시나리오를 4개로 셌습니다. 실제로는 2개입니다(A-1·A-2). 사이트와 오리진이 다른 축이라는 걸 문장으로는 알고 있으면서, 세는 순간 다시 하나로 묶은 것입니다.

서브도메인만 등록 도메인 2개
CORS preflight · Vary: Origin 걸린다 걸린다
Allow-Origin: * + credentials 충돌 재현됨 재현됨
SameSite=None · 서드파티 쿠키 차단 재현 불가 재현 가능

CORS 쪽 셋은 도메인 하나로 전부 재현됩니다. 실제로 B-1·B-2·B-3을 그렇게 실측했습니다. 그래서 두 번째 도메인을 사지 않기로 되돌렸습니다 → ADR-018

조사가 설계를 바꿨고, 실측이 그 조사를 다시 바꿨습니다. 문서를 고치면서 남긴 이유가 이겁니다 — 첫 번째 오해(오리진=사이트)는 조사로 잡혔지만, 두 번째 오해(그러니 CORS도 못 한다)는 조사로 안 잡히고 재 봐야 잡혔습니다.

3-2. "서드파티 쿠키가 곧 사라진다"는 전제가 무너졌다

면접 예상질문으로 준비했던 답인데, 표준 확인 과정에서 뒤집혔습니다.

시점 사건
2024-07 Google, Chrome 서드파티 쿠키 폐지 계획 철회
2025-04 별도 프롬프트 도입도 취소
2025-10 Privacy Sandbox 대부분 종료 (Topics, Protected Audience, Attribution Reporting API 포함)
2026-01 → 07 해당 API deprecation → 제거

Safari·Firefox는 여전히 차단합니다. 그래서 문제는 "쿠키가 사라진다" 가 아니라 "브라우저마다 다르다" 로 재정의됩니다. 서버사이드 전송은 쿠키 폐지 대비가 아니라 브라우저 편차 대응입니다.

3-3. 매체가 10종 이상이었다 → 채널 어댑터가 요구사항이 됨

플랫폼 A에 붙어 있는 것을 실측했습니다(개수만 표기).

계층 개수
태그 관리 컨테이너 3개
분석 1
검색·디스플레이 전환 ID 10개 이상
소셜 픽셀 2
DSP·리타게팅 2
국내 전용 분석 1
글로벌 소셜 (한 매체는 픽셀 3개) 2

전환 ID가 10개 넘는다는 건 캠페인·계정이 계속 추가된다는 뜻입니다. "광고 매체 연동"은 일회성 구축이 아니라 매체 추가·교체가 상시 발생하는 운영입니다. → ADR-005

3-4. 재화 만료 규칙이 스키마를 강제했다

플랫폼 A 이용약관에 유료 재화 5년 / 무료·이벤트 재화 1년이 명시돼 있습니다.

만료가 다른 재화를 잔액 컬럼 하나로 합치면 어느 재화가 언제 만료되는지 복원할 수 없습니다. 지급 건별 원장(lot) + expires_at + 소진 순서가 필요합니다.

이 규칙은 UI를 아무리 봐도 안 나오고 약관에만 있습니다. 약관을 읽은 이유가 이것입니다. → ADR-006

3-5. 보존기간이 20배 차이 났다 → 테이블 분리의 이유가 법이 됨

데이터 보존 근거법
웹사이트 방문기록 3개월 통신비밀보호법
결제·계약 기록 5년 전자상거래법·전자금융거래법

어트리뷰션 원본(방문)과 전환 결과(결제)는 같은 파이프라인의 앞뒤인데 파기 주기가 20배 다릅니다. 섞으면 파기 배치가 불가능해집니다. 여기서 정규화의 이유는 성능이 아니라 법입니다.

동시에, 방침에 "가입 경로"가 수집 항목으로 명시돼 있습니다 = 어트리뷰션 결과가 회원 레코드에 5년 남습니다. 그래서 users에 유입 경로를 스냅샷으로 복사합니다 — 원본이 3개월 뒤 사라져도 살아남아야 하므로 비정규화가 정당화됩니다.

3-6. 읽기 복제본 운영 흔적 → 복제 지연 재현 추가

플랫폼 A 응답 헤더에서 세션을 읽기 복제본 중 하나에 고정하는 쿠키를 발견했습니다(이름에 복제본 번호가 들어감).

복제 지연으로 인한 read-after-write 이상을 사용자 단위 고정으로 막는 구조입니다. 공고의 MySQL 태그가 단일 DB에 쿼리 짜는 일이 아니라는 뜻이라, 이 프로젝트에도 읽기/쓰기 커넥션 분리 + 지연 주입 재현을 추가했습니다. → ADR-007

3-7. 리다이렉트 브리지가 실제 패턴이었다

플랫폼 A는 작품 ID만 받아 적절한 회차로 보내는 리다이렉트 컨트롤러를 두고, 배너 링크를 전부 그리로 통과시킵니다. 광고 링크를 회차와 무관하게 유지하고, 리다이렉트 지점에서 유입 파라미터를 서버가 가로챌 수 있기 때문으로 보입니다.

초안의 302 리다이렉트 항목이 실제 운영 패턴과 일치한다는 확인이었습니다.

3-8. 어트리뷰션 분류가 3단이었다

유입 지점을 pid(매체·파트너) / subpid(캠페인·소재) / channel(유입 채널) 3단으로 나누고, 이를 가입시점과 최종 두 벌로 보존합니다. 이 프로젝트도 같은 어휘를 씁니다.

3-9. 프레임워크 확인

세션 쿠키 이름(...ci_session 형태)과 /{controller}/{method}/{key}/{value} URL 구조로 CodeIgniter가 확인됐습니다. 공고가 CodeIgniter를 요구한 것이 근거 있는 요구였다는 뜻입니다. → ADR-001


4. 대조군에서 얻은 것 — 플랫폼 B

플랫폼 B는 공개 JSON API가 인증 없이 열려 있어 실제 운영 스키마를 그대로 볼 수 있었습니다. 여기서 얻은 설계 판단입니다.

관측 설계 결론
작가가 writers / painters / novelOriginAuthors 3역할, 역할이 배열 작가는 다대다 + role. 한 사람이 겸업 가능
연재요일이 배열 (publishDayOfWeekList) 요일은 별도 테이블. 단일 컬럼 불가
연령등급이 {type: "RATE_15", description: "..."} enum + 표시문구 — 그런데 목록 API엔 레거시 boolean도 병존 처음부터 enum으로 시작. 외부 API 필드는 한번 나가면 못 지운다는 실물 증거
회차가 전역 ID + 작품 내 순번 둘 다 순번은 재배열로 바뀌므로 안정적 참조는 전역 ID
공개일이 "20.11.01" 표시용 문자열 반면교사. 다국어·타임존 변환 불가 → 저장·전송은 UTC ISO 8601
개인화 필드(읽음 여부·찜)가 콘텐츠 목록 API에 섞여 있음 응답을 공용 캐시할 수 없다. 콘텐츠와 개인화를 분리하면 CDN 캐시가 산다
국가 판정 후 규제별 플래그 3종(EU·캘리포니아·아동)을 쿠키로 내려줌 동의 로직 하나로 셋을 처리할 수 없다. 기본값이 opt-in / opt-out으로 반대

그리고 전제 하나가 반박됐습니다

처음엔 "웹툰이니까 조회수·별점·랭킹 UI가 기본"이라고 생각했습니다.

두 플랫폼 모두 조회수를 공개하지 않습니다. 플랫폼 B는 API에 viewCount 필드가 있는데 항상 0으로 내려줍니다. 랭킹(순위)은 노출하되 원본 지표는 감춥니다.

한 곳만 봤으면 "그 회사가 안 넣은 것"으로 오해했을 텐데 두 곳이 독립적으로 같은 선택을 했으니 구조적 이유가 있습니다. → 이 프로젝트에서 조회수 기능을 뺐습니다.

i18n 코드 체계 — 어느 쪽도 ISO만으로 안 됐다

URL 경로 hreflang
플랫폼 A 비표준 슬러그 (국가코드·3글자 약어 혼용) 표준 BCP 47
플랫폼 B 문자체계 기반 지역 기반

정반대 방향으로 어긋났습니다. 공통 결론: URL 슬러그 ↔ BCP 47 매핑 테이블은 반드시 필요하고, 중국어는 언어코드만으로 처리 불가(간체/번체는 문자체계 축). 이것도 두 곳에서 독립 재현됐기에 구조적 문제로 판단했습니다.


5. 표준은 기억이 아니라 확인으로

설계에 쓰는 규격은 버전과 현재 상태를 직접 확인했습니다. 기억으로 답했으면 틀렸을 것이 둘 있었습니다.

확인 항목 결과
서드파티 쿠키 / Privacy Sandbox 폐지 철회 + Sandbox 종료 (3-2)
PCI DSS v4.0.1이 현행, 미래시점 요구 51개가 2025-03-31부로 전부 의무화
IAB TCF v2.3이 2026-03-01부터 의무
CHIPS Partitioned SameSite=None; Secure와 반드시 함께 써야 효력

적용한 표준은 데이터 타입까지 내려옵니다.

항목 결정 표준
언어 / 국가 / 통화 CHAR(2) / CHAR(2) / CHAR(3) ISO 639-1 / 3166-1 / 4217
금액 정수 minor unit (FLOAT 금지) ISO 4217 exponent — KRW·JPY는 소수 0자리
시각 DATETIME(3) UTC + zone id 별도 ISO 8601 / IANA tzdb
문자셋 utf8mb4 MySQL의 utf8은 3바이트라 이모지 깨짐
에러 응답 application/problem+json RFC 9457
쿠키 판정 eTLD+1 기준 RFC 6265bis

6. 조사가 만든 규율 — 버린 것이 남긴 것보다 많다

조사를 하면 넣고 싶은 게 늘어납니다. 그래서 버릴 것을 먼저 확정했습니다.

버린 것 24개 (일부): 뷰어, 요일 연재 화면, 랭킹 집계, 검색, 조회수·별점, 댓글, 추천, UGC 등급, 큐레이션 태그, 본인확인 실연동, 소셜 로그인 4종, 소장/대여 만료, 기다무 로직, 이벤트 3종, 선물함, 구독 상태 머신, PG 실연동, IAP, 환불 금액 계산, 다중 PG 어댑터, Kubernetes·ECR, 다중 AZ, IaC

남긴 것 9개: 파라미터→쿠키→서버, 리다이렉트 브리지, 크로스사이트 수집, 채널 어댑터+아웃박스, GA4 서버사이드 전송, Meta CAPI, 결제 전환+재화 원장, 인덱스·SKIP LOCKED+구간 계측, 읽기/쓰기 분리

기준은 하나였습니다 — 공고 스킬 태그를 하나도 빠뜨리지 않으면서, 조사로 근거가 확보된 것만.


7. 조사 윤리

  • 로그인·회원가입·결제를 수행하지 않았습니다. 자격증명을 입력하지 않았습니다
  • 동의·쿠키 배너를 임의 수락하지 않았습니다
  • CAPTCHA 우회, robots.txt 무시, 요청 폭주, 크롤러 제작을 하지 않았습니다
  • 접근이 막힌 항목은 우회하지 않고 [미확인]으로 남겼습니다
  • 약관·방침은 원문 인용이 아니라 항목 구조만 추출했습니다
  • 이 문서에는 추적 식별자·서버 주소·내부 경로 같은 특정 값을 싣지 않았습니다

전부 브라우저를 열면 누구나 볼 수 있는 공개 응답 범위입니다. 그럼에도 특정 서비스의 식별자를 공개 저장소에 카탈로그로 남기는 것은 다른 문제라고 판단해, 원본 조사 기록은 비공개로 두고 이 요약만 공개합니다.