Skip to content

Latest commit

 

History

History
108 lines (67 loc) · 7.88 KB

juseong.md

File metadata and controls

108 lines (67 loc) · 7.88 KB

이주성

자기소개

안녕하세요. 마리너 1기 이주성입니다.

이번 프로젝트에서는 GitHub Flow를 기반으로 Issue, 브랜치, 커밋, Pull Request, 코드 리뷰가 연결되는 협업 과정을 연습하고 있습니다. 동시에 웹 애플리케이션에서 자주 등장하는 라우팅, 인증·인가, 상태 관리 개념을 정리하며 프론트엔드와 백엔드, 데이터베이스가 어떻게 역할 분담하는지 이해하고 있습니다.


공부한 내용

1. Same-Origin, CORS, Proxy

브라우저에는 Same-Origin Policy가 있습니다. 프로토콜·호스트·포트가 다르면 서로 다른 origin으로 보고, 기본적으로 스크립트가 다른 origin의 응답을 마음대로 읽지 못하게 합니다.

  • CORS (Cross-Origin Resource Sharing): 서버가 특정 origin의 접근을 허용하겠다고 알려주는 메커니즘입니다. 프론트와 백엔드 포트가 다르면 CORS 설정이 필요해질 수 있습니다.
  • 개발용 Proxy: 로컬에서 프론트 개발 서버가 /api 같은 경로를 백엔드로 대신 전달하면, 브라우저는 같은 origin으로 요청한다고 인식합니다. Proxy가 CORS 정책 자체를 해제하는 것은 아니며, 브라우저가 같은 origin의 개발 서버로 요청하면 개발 서버가 이를 백엔드로 전달하는 방식입니다. 이 설정은 보통 로컬 개발 전용이며, 프로덕션 빌드에는 포함되지 않습니다.
  • 배포 환경의 Rewrites: 배포 플랫폼이 하나의 도메인 안에서 경로별로 정적 파일과 API를 나누어 응답하면, 브라우저 입장에서는 same-origin이 되어 CORS 이슈를 줄일 수 있습니다.

핵심은 “로컬에서 포트를 나누는 방식”과 “배포에서 경로로 나누는 방식”이 다르다는 점입니다.

2. 서버리스와 Cold Start / Warm Start

서버리스는 요청이 들어올 때 함수(런타임)를 실행하는 방식입니다. 항상 켜져 있는 서버를 직접 운영하지 않는 대신, 다음 특성을 이해해야 합니다.

  • Cold Start: 오랫동안 호출이 없다가 새로 런타임을 띄울 때 발생하는 초기 지연
  • Warm Start: 이미 떠 있는 런타임을 재사용해 비교적 빠르게 응답하는 상태
  • Stateless: 인스턴스 로컬에 중요한 상태를 오래 저장하기 어렵다는 전제. 세션 검증·데이터 저장은 외부 서비스(인증 서버, DB)에 맡기는 설계가 자연스럽습니다.

3. 방어의 계층화 (Defense in Depth)

보안은 한 곳에서만 막으면 부족합니다. 계층을 나눠 역할을 분리하는 것이 기본입니다.

계층 개념 역할 한계
UI Guard 라우트 가드 로그인하지 않은 사용자의 화면 진입을 막음 브라우저 조작이나 API 직접 호출은 막지 못함
API Auth 서버 인증 요청의 토큰/세션을 검증하고 실패 시 401 반환 DB 권한 설계가 약하면 여전히 위험할 수 있음
DB Security GRANT + RLS 테이블/행 단위로 접근 범위를 제한 정책이 잘못되면 과도한 차단 또는 과도한 개방이 발생

UI만으로 보안을 끝냈다고 생각하면 안 됩니다. 최종 권한 판단은 서버와 DB에서 해야 합니다.

4. 인증(Authentication)과 인가(Authorization)

  • 인증: “이 사용자가 누구인가”를 확인하는 과정
  • 인가: “이 사용자가 이 자원에 접근해도 되는가”를 확인하는 과정

토큰이 있다고 해서 바로 신뢰하면 안 됩니다. 특히 JWT는 페이로드가 Base64로 인코딩되어 있어 디코드만으로 내용을 읽는 것과 서명을 검증하는 것은 전혀 다릅니다.

  • 위험한 접근: 클라이언트/서버에서 토큰을 단순 디코드해 user_id를 꺼내 사용
  • 안전한 접근: 인증 서버에 토큰을 다시 물어보거나, 공개키(JWKS)로 서명을 검증하고 만료·폐기 여부까지 확인

서버리스처럼 상태를 오래 들고 있기 어려운 환경에서는, 인증 서버를 **단일 진실 공급원(SSOT)**으로 두고 세션 유효성을 위임하는 방식이 자주 사용됩니다.

5. Bearer 토큰과 API 키 권한

  • Bearer Token: Authorization: Bearer <token> 형태로 전달되는 자격 증명. “이 토큰을 가진 요청을 허용한다”는 의미입니다.
  • Publishable / Anon Key: 브라우저에 노출될 수 있는 저권한 키. 반드시 RLS 같은 행 단위 보안과 함께 써야 안전합니다.
  • Service Role Key: RLS를 우회할 수 있는 고권한 키. 프론트엔드에 노출하면 안 되며, 필요할 때도 사용 범위를 최소화해야 합니다.

6. GRANT와 RLS

데이터베이스 보안은 보통 두 층으로 이해합니다.

  • GRANT: 테이블(또는 스키마) 단위의 기본 권한. “이 역할이 이 테이블을 SELECT/INSERT 할 수 있는가”
  • RLS (Row Level Security): 행 단위 필터. 예: auth.uid() = user_id라면 “내 행만” 보이게 됩니다.

비유하면 GRANT는 건물 출입문, RLS는 각 호실 도어락입니다. 한쪽만 있으면 전체가 막히거나, 반대로 전체가 열릴 수 있습니다.

7. 요청 추적과 입력 검증

API 한 건을 안정적으로 다루려면 다음 개념이 중요합니다.

  • Request ID: 요청마다 고유 ID를 붙여 로그·에러·타임아웃을 같은 흐름으로 추적
  • 스키마 검증: 요청 body의 타입, 길이, 공백 여부를 서버에서 다시 검사 (클라이언트 검증만으로는 부족)
  • 타임아웃: 외부 AI/HTTP 호출처럼 느린 작업에는 상한 시간을 두어 서버 자원을 보호
  • 컨텍스트 윈도우: 대화형 API에서는 최근 N건만 가져와 모델 입력 크기를 제한하는 패턴이 흔함

8. 프론트엔드 상태와 동시성

  • Optimistic UI: 서버 응답을 기다리기 전에 화면에 먼저 반영해 체감 속도를 높이는 기법. 실패 시 롤백 전략이 필요합니다.
  • useRef와 중복 요청 방지: React state 갱신은 렌더 사이클을 거치므로, 연속 클릭처럼 즉시 막아야 하는 플래그는 동기적으로 바뀌는 ref가 적합할 수 있습니다.
  • sessionStorage: 탭 단위 UI 복원용 캐시에 가깝습니다. 서버가 가진 진짜 데이터(문맥, 권한)를 대체하지 않습니다.
  • 네임스페이스 키: 캐시 키에 user.id를 넣어 계정 전환 시 이전 사용자 데이터가 섞이지 않게 합니다.
  • 상태 수명 분리: 전역이 필요한 것(인증)과 지역적인 것(채팅 입력/말풍선)을 나누면 불필요한 리렌더와 복잡도를 줄일 수 있습니다.

9. Liveness와 Readiness

헬스체크는 목적이 다릅니다.

  • Liveness (/health): 프로세스가 살아 있는지. 죽어 있으면 재시작 대상으로 봅니다.
  • Readiness (/ready): DB, 외부 API 키처럼 의존성까지 준비됐는지. 살아 있어도 준비가 안 됐으면 트래픽을 받으면 안 됩니다.

이 구분을 두면 “앱은 떠 있지만 DB가 죽은 상태”를 더 정확히 격리할 수 있습니다.

10. MVP에서의 데이터 모델링 선택

초기에 복잡한 다대다 관계와 조인을 모두 넣기보다, 조회 패턴이 단순한 단건/로그성 테이블로 시작하는 경우가 있습니다. 문맥 조회가 “최근 성공 기록 N건”처럼 단순하다면, 테이블을 과도하게 정규화하지 않는 것도 의도적인 설계 선택이 될 수 있습니다. 대신 나중에 확장할 때 마이그레이션 비용을 감수해야 합니다.


이번 프로젝트에서 맡은 내용

  • 웹 아키텍처·보안 관련 개념 학습 노트 작성
  • docs/CONTRIBUTING.md 금지 사항·협업 흐름·관련 문서 섹션 추가 작업
  • Issue 기반 작업과 PR 연동 실습