바이브 코딩으로 Electron OAuth 구현하기

조영록·2025년 7월 31일
1
post-thumbnail

"혼자 하는 사이드 프로젝트에서 AI와 페어 프로그래밍하니까 혼자서는 절대 발견 못했을 문제들이 보이더라"

들어가며: 사이드 프로젝트의 현실

개인 사이드 프로젝트로 Electron 기반 비디오 편집 앱을 만들고 있었습니다. Agent Server(Fastify + Prisma)와 Electron 앱이 잘 돌아가고 있었는데, "사용자 로그인 기능을 추가해볼까?"라는 생각이 들었습니다.

기존 아키텍처:

  • Electron 앱 (Next.js): 사용자 인터페이스
  • Agent Server (Fastify): 비즈니스 로직, 파일 처리
  • Database (PostgreSQL + Prisma): 데이터 저장

혼자 하는 프로젝트라 요구사항도 없고, 일정도 자유롭고... "그냥 Google 로그인 하나 붙이면 되겠네"라고 가볍게 생각했죠. 하지만 현실은 달랐습니다.

1장. 바이브 코딩의 시작: 문제 정의부터

🤔 "뭘 구현하려는 거지?" - 요구사항 정의하기

구글링부터 시작하지 않고, 클로드 코드에게 먼저 물어봤습니다:

: "Electron 앱에 Google 로그인을 추가하려고 하는데, 뭘 고려해야 할까요?"

클로드: "구체적인 아키텍처와 보안 요구사항을 먼저 정리해보겠습니다."

: "좋아요. ultrathink 모드로 분석해주세요."

이때부터 AI와 함께 요구사항을 체계적으로 정리하기 시작했습니다:

📋 정의된 요구사항들:
1. 사용자가 Google 계정으로 로그인
2. Agent Server API 호출 시 인증된 사용자만 접근
3. 로그인 상태 유지 (앱 재시작 후에도)
4. 보안: 토큰이 안전하게 저장되어야 함
5. UX: 복잡하지 않은 로그인 플로우

바이브 코딩의 첫 번째 장점: 혼자 고민할 때는 "구현 방법"부터 찾아보게 되는데, AI와 대화하니까 "무엇을 구현할지"부터 명확하게 정리할 수 있었습니다.

💡 ultrathink 활용 팁: 클로드 코드에서만 작동하는 이 기능은 일반 chat보다 CLI 환경에서 코딩할 때 진가를 발휘합니다. 복잡한 아키텍처 결정이나 보안 분석이 필요할 때 "ultrathink" 키워드를 사용하면 최대 31,999개 토큰의 사고 자원을 할당받아 더 심층적인 분석을 받을 수 있습니다.

🧠 ultrathink: 내가 발견한 클로드 코드의 숨겨진 무기

이때쯤 클로드 코드의 ultrathink 기능을 적극 활용하기 시작했습니다. 이는 단순한 키워드가 아니라 클로드 코드에 내장된 실제 기능으로, 사고 예산(thinking budget) 시스템을 통해 작동합니다. Anthropic 공식 문서에 따르면 "think" < "think hard" < "think harder" < "ultrathink" 순으로 점진적으로 더 많은 계산 자원을 할당합니다.

실제로 ultrathink는 31,999개의 토큰을 할당받아 가장 복잡한 문제 해결을 위한 최고 수준의 사고 자원을 제공합니다.

: "현재 아키텍처에서 OAuth를 구현하는 방법들을 ultrathink하여 비교 분석해주세요."

클로드의 ultrathink 결과:

방법 1: Agent Server 중심 OAuth
- 장점: 기존 아키텍처 활용
- 단점: Electron ↔ Agent Server 토큰 전달 이슈

방법 2: Electron 직접 OAuth  
- 장점: 보안성
- 단점: 복잡도 증가

방법 3: 하이브리드 접근
- 분석 필요: 각 단계별 보안 고려사항

구글링 vs AI 대화의 차이: 구글링했다면 "Electron OAuth tutorial"을 찾아서 따라했을 텐데, AI와 대화하면서 "ultrathink 해주세요"라고 요청하니까 내 특정 상황에 맞는 여러 옵션들을 체계적으로 분석할 수 있었습니다.

2장. 첫 번째 벽과 임시방편: "일단 되게 만들자"

💥 "next is not a function" - 첫 번째 장애물

가장 "당연해 보이는" 방법부터 시도했습니다:

// 🤔 "이게 표준 방법이겠지?"
server.get('/auth/google', passport.authenticate('google', {
  scope: ['profile', 'email']
}));

결과:

{"statusCode":500,"error":"Internal Server Error","message":"next is not a function"}

🔧 빠른 임시 해결: "일단 작동하게 만들자"

: "왜 이 에러가 날까요?"

클로드: "Fastify와 Passport.js의 호환성 문제입니다. Promise로 래핑하면 해결됩니다."

// 🔧 임시방편 - 일단 에러는 사라짐
server.get('/auth/google', async (request, reply) => {
  return new Promise((resolve, reject) => {
    passport.authenticate('google')(request, reply, (err) => {
      if (err) reject(err);
      else resolve();
    });
  });
});

이때의 마음가짐: "좋아, 에러 없어졌네! 다음 단계로 넘어가자"

사실 이때는 큰 생각 없이 클로드 코드에게 맡겨두고
. 바이브 코딩이라고 하기엔 너무 수동적이었죠.

3장. OAuth 플로우의 미스터리: "구글이 뭘 어디로 보내는 거지?"

🤯 OAuth 플로우 이해하기: 끝없는 질문의 연속

이때부터 문제가 생겼습니다. 코드는 작동하는 것 같은데 뭔가 이상했어요. 그리고 OAuth가 정확히 뭘 하는 건지 이해가 안 되기 시작했습니다.

: "리디렉트 uri가 'http://localhost:3003/api/auth/google/callback'가 되어서는 안되지 않나요?"

클로드: "Google에서 설정한 리디렉트 URI와 일치해야 합니다."

: "좋습니다. 불필요한 서버 중간 통신은 말씀하신대로 제거합시다. 그런데, 그 전에 궁금한 것이, 커스텀 프로토콜을 gcp에서 직접 등록할 수가 있나요? 이 부분은 이해가 잘 안 가네요."

🔄 반복되는 의문과 확인: "정말 이해했나?"

이때부터 똑같은 플로우에 대해 계속 다른 방식으로 질문하기 시작했습니다. 뭔가 납득이 안 되는 부분이 있었거든요:

: "좋아요. 그런데, 그럼 1단계에서 agent-server가 구글 로그인 api를 next 클라이언트 측에 노출하되, 클아이언트에게 구글 로그인 페이지로 리다이렉트 시켜주는 역할만 하는거죠? 그 이후에 구글이 agent-server의 oauth/callback 엔드포인트를 호출해주고, 그때, agent-server가 일렉트론 클라이언트에게 인증값을 전달해주는 거구요? 잘 이해했나요? 보안적인 문제는 없을까요?"

클로드: "플로우를 정확히 이해했습니다. 하지만 마지막 단계에서 토큰 전달 방식에 보안 이슈가 있을 수 있습니다."

하지만 여전히 뭔가 이상했습니다. 다시 질문:

: "1단계와 5단계는 살짝 이해가 안 가요. 기존의 agent-server가 이 역할을 해도 상관이 없지 않을까요? 추후 msa와 같이 관심사 분리를 하기 위해 임시 서버가 처리해도 괜찮을까요? agent-server도 토큰이 올바른 토큰인지는 알 방법은 있어야 할텐데요?"

또 다른 각도에서 질문:

: "좋아요. 클라이언트에서 직접 브라우저를 띄어서 거기서 구글 서버로 로그인하고, 에이전트 서버는 나중에 그 인증값만 확인하는 건가요? 승인된 리디렉션 url은 사용자가 브라우저를 통해 로그인 성공하면 거기로 보내는 url인거죠?"

🤔 "플로우가 이해가 안 되네요"

결국 솔직하게 말했습니다:

: "플로우가 이해가 안 되네요."

이때 또 다른 의문이 생겼습니다:

: "그런데 궁금한 것은 일렉트론 앱에서 next로 ui를 구성하고 있지만, 거기서 window.electron 과 같은 코드를 쓰고 있으므로, 이거 next 서버로 배포 못하지 않나요? ssg 빌드 말고 서버 배포 방식도 되나요? 만약 그러하다면 이 앱은 사실상 서버가 2개인 건가요?"

💡 이해하려는 집요함의 가치

왜 계속 같은 걸 다시 물어봤을까?

돌이켜보니 OAuth 플로우가 단순히 "구글에서 로그인하면 토큰 준다"가 아니라는 걸 본능적으로 느꼈던 것 같습니다. 각 단계마다:

  • Google은 뭘 하는가?
  • Agent Server는 뭘 하는가?
  • Electron은 뭘 하는가?
  • 각 단계에서 뭐가 어디로 전달되는가?
  • 왜 이렇게 복잡한가?
실제 Google OAuth 플로우 (드디어 이해한 버전):
1. 사용자: "구글 로그인" 버튼 클릭
2. 내 앱: Google 인증 URL 생성하여 브라우저로 리디렉트
3. Google: 사용자에게 로그인 요청
4. 사용자: Google에서 로그인하고 권한 허용
5. Google: authorization code를 내 앱의 콜백 URL로 전송
6. 내 앱: authorization code를 access token으로 교환
7. 내 앱: access token으로 Google에서 사용자 정보 요청
8. 내 앱: 받은 사용자 정보로 로그인 처리

반복 질문의 진짜 이유: OAuth가 8단계나 되는 복잡한 플로우인데, 처음에는 "그냥 로그인하면 끝"이라고 생각했으니까 계속 이상했던 거였습니다.

질문을 통한 학습의 효과: 단순히 "OAuth 구현법"을 찾아보는 게 아니라 각 단계를 하나씩 이해하면서 내 아키텍처에 어떻게 적용되는지 파악할 수 있었습니다. 그리고 이해할 때까지 계속 질문하는 것이 얼마나 중요한지도 깨달았습니다.

🧠 시스템 2 사고의 시작

여기서 시스템 2 사고가 시작되었습니다. 심리학자 다니엘 카너먼이 제시한 개념으로, 시스템 1(빠르고 직관적)과 달리 시스템 2는 느리지만 신중하고 논리적인 사고 방식입니다.

  • 시스템 1: "OAuth? 그냥 passport.js 쓰면 되지"
  • 시스템 2: "내 Electron 아키텍처에서 각 단계별로 보안은 어떻게 보장하지? 토큰은 어디서 어떻게 저장하지?"

바이브 코딩의 진화: 이때부터 "빠른 구현"보다는 "올바른 이해"가 우선이 되었습니다.

4장. 진짜 문제의 발견: "어? 토큰이 안 온다?"

🤷‍♂️ 뭔가 이상한데...

OAuth 플로우는 작동하는 것 같았는데, 계속 뭔가 이상했습니다:

[로그] Google OAuth 콜백 수신
[로그] 토큰 교환 성공  
[로그] 사용자 정보 획득
[UI] OAuth 초기화에 실패했습니다...

실제 에러 메시지들:

일단 로그인 시도 시에 http://localhost:3000/login/?error=oauth_init_error 경로로 보내는데, 
이 경로에 대해서 404 에러가 발생하네요?
OAuth 초기화에 실패했습니다. Google OAuth 설정을 확인해주세요 라는 메시지가 ui 상에 표시되네요.

💡 뭔가 이상하다는 의심

이때 뭔가 이상하다고 의심하기 시작했습니다:

"어? 문제가 Passport.js 호환성이 아니라 Agent Server에서 Electron으로 토큰 전달 쪽에 있는 건 아닐까?"

: "앱을 실행했을때, url을 올바른 경로로 리다이렉트하지는 않는 것 같습니다."

이때부터 진짜 체계적인 접근이 시작되었습니다. 코드 수정하기 전에 구현 계획부터 제대로 토론하기 시작했죠.

🔄 바이브 코딩 패턴의 변화

Before (2장): 수동적 해결

문제 발생 → AI에게 해결책 요청 → 코드 적용 → 다음 단계

After (4장부터): 능동적 분석

문제 발생 → "뭔가 이상한데?" → 구현 계획 토론 → ultrathink 분석 → 근본 원인 파악

핵심 차이점:

  • 코드 수정보다 문제 이해가 우선
  • "일단 되게 만들자"에서 "왜 안 되는지 정확히 알자"로 전환
  • AI를 코드 생성기가 아닌 사고 파트너로 활용

이때부터 의식적이고 체계적인 접근이 시작되었습니다!

5장. 토큰 전달의 딜레마: 클로드의 제안과 나의 의심

🤷‍♂️ "어? 토큰을 어떻게 전달하지?"

OAuth 인증은 성공했는데, 새로운 문제가 생겼습니다.

: "Agent Server에서 토큰을 받았는데, 이걸 Electron 앱에 어떻게 전달하죠?"

클로드: "몇 가지 방법이 있습니다. Exchange Code 방식을 제안해보겠습니다."

💡 클로드의 Exchange Code 제안

클로드의 첫 번째 제안:

// 🟡 Exchange Code 방식 (클로드가 제안)
const exchangeCode = crypto.randomUUID();
global.authTokens.set(exchangeCode, { token, user });

return reply.redirect(`/auth/complete?code=${exchangeCode}`);

클로드: "실제 토큰 대신 일회용 코드를 전달하는 방식입니다. 보안상 더 안전합니다."

🤔 내가 먼저 보안을 의심하기

그런데 실제로 구현해보니 계속 실패했습니다. 그때 제가 먼저 뭔가 이상하다고 느꼈습니다:

: "음.. 현재 구현 계획에서 토큰이 url로 노출된다면, postmessage 브릿지나, 쿠키 방식을 고려해야 할 것 같아요. 그런데, postmessage 브릿지는 서버가 토큰을 감추어 전송하는 것과는 관계 없지 않나요? 결국 쿠키를 써야 하는 걸까요? 로그인 기능은 보안과 직결되었기 때문에, 약간 오버 엔지니어링이라 생각되어도, 확실하게 가야 할 것 같아요. ultrathink하여 결론을 유도합시다."

클로드: "매우 중요한 보안 포인트를 지적했습니다. 분석해보겠습니다."

그제서야 보안 위험들이 정리되기 시작했습니다:

  • URL 히스토리에 토큰 저장 위험
  • 서버 로그에 토큰 기록 위험
  • XSS 공격 시 토큰 탈취 가능성
  • 타이밍 이슈로 인한 불안정성

🔄 쿠키와 XSS 공격 고민

: "하지만 이것도 xss 스크립트 공격 시 세션 id를 탈취당할 위험이 있지 않아요? best practice는 어떻게 되어 있나요? 정 어렵다면, 교환 코드 방식으로 가되, 구글 로그인한 사용자와 동일한 로그인 환경인지 체크하고, 혹시 두번 연속으로 교환 api를 호출했다면 즉시 해당 세션을 무력화시키는 방식으로 가야 하나 싶기도 하네요.. 충분히 ultrathink 해주세요."

체계적 접근의 함정: 혼자였다면 "작동 안 하네, 다른 방법 찾아보자"로 끝났을 텐데, 내가 먼저 보안을 의심하고 질문하니까 심각한 이슈들이 보이기 시작했습니다.

6장. 업계 표준 분석: VS Code와 Discord는 어떻게 했을까?

🔬 업계 표준 분석 요청

: "음 커스텀 프로토콜이 가장 권장되는 방식이라는 건 알겠어요. 그런데, 구글 oauth에서 커스텀 프로토콜로 리다이렉트하는 걸 허용하는지 모르겠네요. 사용자가 브라우저를 통해 로그인 하면, 리다이렉트로 최종적으로는 커스텀 프로토콜로 보내주더라도, 그걸 중계하기 위한 주소가 따로 마련되어야 하지 않을까요? 제가 구글 oauth 와 현재 아키텍처를 잘 이해했는지 megathink 하여 정리하고 의견을 내주세요."

클로드: "실제 업계에서 어떻게 해결하고 있는지 분석해보겠습니다."

🎯 VS Code 방식 발견

: "좋아요. 그럼 마지막에 일렉트론 앱으로 토큰을 전달하기 위한 전략을 일렉트론 커뮤니티와 공식 문서를 참고하여 어떤 방법이 좋을지 megathink하여 검토해주세요."

클로드의 megathink 분석:

VS Code 방식 (Main Process HTTP Server):
✅ 완전한 토큰 격리: 렌더러 프로세스 미경유
✅ Agent Server 불필요: 직접 Google과 통신  
✅ XSS 완전 차단: 토큰이 웹 컨텍스트에 없음
✅ 구현 단순: HTTP 서버 하나만 추가

Discord 방식 (Deep Link):
✅ Agent Server 재활용: 기존 구조 유지
✅ 토큰 완전 격리: Deep link는 main process로만
✅ 사용자 경험: 자동으로 앱으로 전환

하이브리드 방식 (렌더러 경유 + 보안 강화):
✅ 기존 구조 최대 활용
✅ 디바이스 핑거프린팅으로 세션 추적
✅ 이중 호출 시 즉시 세션 무효화
⚠️ 렌더러 경유로 인한 XSS 위험 잔존

💡 내가 제안한 하이브리드 방식

실제로는 내가 렌더러 레이어를 거치되 보안을 극대화하는 방식을 제안했었습니다:

나의 제안:

// 디바이스 핑거프린팅으로 세션 추적
const deviceFingerprint = generateDeviceFingerprint();
const sessionId = crypto.randomUUID();

// 토큰 교환 API 설계
POST /api/auth/exchange-token
{
  "code": "exchange_code",
  "device_fingerprint": "...",
  "session_id": "...",
  "timestamp": "..."
}

// 보안 규칙
- 같은 세션에서 2번 호출 시 즉시 무효화
- 디바이스 핑거프린트 불일치 시 거부
- 5분 이내 호출만 허용

하지만 클로드의 분석을 듣고 VS Code 방식을 선택하게 되었습니다.

🤔 최종 결정 과정

: "좋아요. vscode 방식 좋은데요. 모든 인증을 우리 서버 없이 구글과 로컬 임시 서버끼리만 통신하네요? 그러면 이후 에이전트 서버의 모든 api에서 어떻게 인가 여부를 판단하고, 어떤 계정들이 우리 서비스를 가입했는지 판단할 수 있을까요? 이 부분도 자세히 ultrathink 해주세요."

이 질문이 Token Exchange Pattern의 핵심이었습니다.

7장. 아키텍처 딜레마 해결: Token Exchange Pattern의 탄생

🤔 새로운 의문점들

VS Code 방식을 선택하려고 하니 새로운 질문들이 생겼습니다:

클로드의 ultrathink 분석:

핵심 문제 인식이 완벽합니다!

현재 아키텍처 딜레마:
기존: Electron ↔ Agent Server ↔ Google
VS Code: Electron ↔ Google (Agent Server 우회!)

하지만 이후에는:
Electron ↔ Agent Server (비디오 처리, AI 기능)

Agent Server가 물어볼 것: "너 누구야? 인증된 사용자야?"

💡 Token Exchange Pattern 도출

이런 질문들을 통해 최종 해결책이 나왔습니다:

[Electron Main Process] ←→ [Google OAuth] (Google 토큰 획득)
         ↓
[Agent Server Token Exchange API] (Google 토큰 → 우리 JWT)
         ↓  
[이후 모든 API 호출] (우리 JWT로 인증)

🏗️ Token Exchange API 설계

최종 API 설계:

// 1. Google 토큰 → JWT 교환
POST /api/auth/google-exchange
{
  "google_tokens": {
    "access_token": "ya29.a0...",
    "refresh_token": "1//04...",
    "expires_in": 3600
  },
  "user_info": {
    "id": "12345",
    "email": "user@example.com",
    "name": "John Doe"
  }
}

Response:
{
  "vivid_tokens": {
    "access_token": "eyJhbGciOiJIUzI1NiIs...", // 우리 JWT
    "refresh_token": "rt_abc123...",
    "expires_in": 900
  },
  "user": {
    "id": "user_vivid_123",
    "email": "user@example.com",
    "name": "John Doe"
  }
}

// 2. JWT 갱신
POST /api/auth/refresh-jwt
{
  "refresh_token": "rt_abc123..."
}

// 3. 로그아웃
POST /api/auth/logout
{
  "access_token": "eyJhbGciOiJIUzI1NiIs..."
}

🔒 보안 강화 사항

Agent Server 보안 검증:

// Origin 검증
const electronOrigin = request.headers['x-electron-origin'];
if (!electronOrigin) {
  return reply.status(403).send({ error: 'Invalid origin' });
}

// Replay Attack 방지
const requestTime = request.headers['x-request-time'];
const timeDiff = Math.abs(Date.now() - parseInt(requestTime));
if (timeDiff > 30000) { // 30초 이내 요청만 허용
  return reply.status(403).send({ error: 'Request timeout' });
}

// Google 토큰 검증
const isValid = await GoogleTokenVerificationService.verify(google_tokens.access_token);
if (!isValid) {
  return reply.status(401).send({ error: 'Invalid Google token' });
}

체계적 접근의 핵심: 단순히 구현 방법을 찾는 게 아니라, 질문을 통해 아키텍처 전체를 고민하고 최적화할 수 있었습니다.

8장. 실제 구현: 체계적 AI 협업으로 코드 작성하기

📝 Main Process OAuth Service 구현

: "이제 구현해보죠. Main Process에서 HTTP 서버를 만드는 코드를 작성해주세요."

// vivid/electron/services/google-oauth.service.ts
export class GoogleOAuthService {
  private server: http.Server | null = null;
  private port: number = 0;

  async startOAuth(): Promise<OAuthResult> {
    // 1. 임시 HTTP 서버 시작
    this.server = http.createServer();
    this.port = await this.getAvailablePort();
    
    return new Promise((resolve, reject) => {
      this.server.listen(this.port, () => {
        // 2. Google OAuth URL 직접 생성
        const authUrl = this.generateGoogleAuthUrl();
        
        // 3. 시스템 브라우저로 OAuth 진행
        shell.openExternal(authUrl.toString());
        
        // 4. 콜백 처리
        this.server.on('request', async (req, res) => {
          try {
            const result = await this.handleCallback(req, res);
            resolve(result);
          } catch (error) {
            reject(error);
          } finally {
            this.cleanup();
          }
        });
      });
    });
  }

🔄 지속적인 피드백과 개선

: "코드가 좋은데, 보안상 놓치고 있는 부분이 있을까요?"

클로드: "몇 가지 개선점이 있습니다..."

// 🔐 보안 강화 코드
private async handleCallback(req: http.IncomingMessage, res: http.ServerResponse) {
  // CSRF 방지 - State 파라미터 검증
  const url = new URL(req.url, `http://localhost:${this.port}`);
  const state = url.searchParams.get('state');
  if (!this.validateState(state)) {
    throw new Error('Invalid state parameter');
  }

  // Origin 검증
  const referer = req.headers.referer;
  if (referer && !referer.startsWith('https://accounts.google.com')) {
    throw new Error('Invalid request origin');
  }

  // 타임아웃 처리
  const timeoutId = setTimeout(() => {
    res.writeHead(408);
    res.end('Request timeout');
  }, 30000);

  try {
    // ... 실제 처리
  } finally {
    clearTimeout(timeoutId);
  }
}

🔒 Token Exchange Service 구현

// vivid/electron/services/token-exchange.service.ts
export class TokenExchangeService {
  static async exchangeGoogleTokens(
    googleTokens: GoogleTokens,
    userInfo: GoogleUserInfo
  ): Promise<TokenExchangeResult> {
    try {
      const response = await fetch(`${AGENT_SERVER_URL}/api/auth/google-exchange`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'X-Electron-Origin': process.pid.toString(),
          'X-Request-Time': Date.now().toString()
        },
        body: JSON.stringify({
          google_tokens: googleTokens,
          user_info: userInfo
        })
      });

      // Google 토큰 즉시 삭제 (보안)
      Object.keys(googleTokens).forEach(key => {
        delete googleTokens[key];
      });

      const result = await response.json();
      return {
        success: true,
        vividTokens: result.vivid_tokens,
        user: result.user
      };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }
}

체계적 접근의 장점: 코드를 작성하면서도 계속해서 보안 검토와 개선을 받을 수 있었습니다.

9장. 마지막 맹점 체크: "정말 안전한가?"

🔍 최종 보안 검증

: "좋습니다. 마지막으로 간과한 보안 문제가 없는지 체크하고, 작업 계획을 세워 봅시다. 충분히 놓치는 내용 없게, 현재 아키텍처에 잘 녹아들게, ultrathink 해주세요."

클로드의 ultrathink 최종 보안 분석:

// ✅ 완전한 토큰 격리 아키텍처
[Main Process] ←→ [Google] (Google 토큰 획득 및 즉시 소각)[Agent Server] (Google 토큰 검증 → JWT 발급)[Secure Storage] (safeStorage로 JWT 암호화 저장)[렌더러 프로세스] (토큰 접근 불가, 사용자 정보만 접근)

🎯 최종 보안 아키텍처

// vivid/electron/services/secure-storage.service.ts
export class SecureStorageService {
  static async storeAuthData(tokens: VividTokens, user: VividUser): Promise<boolean> {
    try {
      const { safeStorage } = require('electron');
      
      if (!safeStorage.isEncryptionAvailable()) {
        throw new Error('Encryption not available');
      }

      const authData = {
        tokens,
        user,
        deviceFingerprint: await this.generateDeviceFingerprint(),
        lastUpdated: new Date().toISOString()
      };

      // JWT 암호화 저장
      const encrypted = safeStorage.encryptString(JSON.stringify(authData));
      const authPath = path.join(app.getPath('userData'), 'auth.encrypted');
      
      await fs.writeFile(authPath, encrypted);
      
      // 메모리에서 토큰 즉시 삭제
      Object.keys(tokens).forEach(key => delete tokens[key]);
      
      return true;
    } catch (error) {
      console.error('[SecureStorage] 저장 실패:', error);
      return false;
    }
  }
}

🔐 최종 보안 검증 포인트

완성된 보안 체계:

  • XSS 완전 차단: 렌더러 프로세스에 토큰 절대 노출 안함
  • CSRF 방지: State 파라미터로 요청 검증
  • Replay Attack 방지: 30초 시간 윈도우
  • Origin 검증: Process ID 기반 요청 인증
  • 암호화 저장: Electron safeStorage 사용
  • 토큰 즉시 소각: 사용 후 메모리에서 완전 삭제
  • Device Fingerprinting: 다중 세션 추적

10장. 체계적 AI 협업 방법론의 핵심

🎯 의식적 개발 접근법의 특징들

1. 의심하는 습관 기르기

  • "어떻게 구현하지?" → "뭘 구현해야 하지?" → "이게 정말 맞나?"
  • "작동하게 만들기" → "안전하게 만들기" → "정말 안전한가?"
  • "빠른 구현" → "올바른 구현" → "놓친 게 없을까?"

2. plan 모드와 ultrathink 적극 활용하기

  • Plan 모드 (Shift+Tab 두 번): 코드 수정 없이 연구와 계획만 세우는 "아키텍트 모드"
  • Auto Accept 모드 (Shift+Tab): 사용자 승인 없이 자동 실행하는 "YOLO 모드"
  • 복잡한 문제일수록 plan 모드로 먼저 분석한 후 실행 여부 결정
  • "ultrathink 해주세요" 명시적 요청으로 심층 분석 유도

3. 반복적 맹점 찾기

  • "이 방법의 문제점이 뭐죠?"
  • "놓치고 있는 보안 이슈가 있을까요?"
  • "다른 접근 방법은 없을까요?"
  • "뭔가 이상한데 ultrathink 해봐"

4. 아키텍처 관점의 사고

  • 부분적 해결책이 아닌 전체 시스템 고려
  • 현재 선택이 미래에 미칠 영향 분석
  • 업계 표준과의 비교

🚀 체계적 접근 vs 전통적 바이브 코딩

전통적 바이브 코딩체계적 AI 협업
직감적 코딩 → 임시방편의식적 분석 → 체계적 솔루션
작동하면 OK보안/확장성까지 고려
문제 발생 시 디버깅문제 발생 전 예방
혼자 고민AI와 체계적 페어 프로그래밍

💡 실제 효과들

1. 혼자서는 발견 못할 문제들 발견

나: "Exchange Code 방식은 어떨까요?"
AI: "여전히 URL 노출 위험이 있습니다."
나: "아, 맞네요. 그럼 다른 방법은?"

2. 체계적인 보안 검토

나: "이 구현에 보안 맹점이 있을까요?"
AI: "다음 5가지 관점에서 검토해보겠습니다..."

3. 업계 표준 학습

나: "VS Code는 어떻게 구현했을까요?"
AI: "VS Code 방식을 분석해서 현재 아키텍처에 적용 방안을 제시해드리겠습니다."

11장. 프로젝트 완성과 성과

📊 최종 구현 결과

보안 수준: 🟢🟢🟢🟢🟢

  • 토큰 완전 격리 (렌더러 프로세스 미경유) ✅
  • XSS 공격 차단 ✅
  • URL/로그 파일 노출 방지 ✅
  • 업계 표준 보안 패턴 적용 ✅

코드 품질: 🟢🟢🟢🟢

  • 확장 가능한 아키텍처 ✅
  • 다른 OAuth 제공자 쉬운 추가 ✅
  • 테스트 가능한 구조 ✅
  • 명확한 책임 분리 ✅

개발 경험: 🟢🟢🟢🟢🟢

  • 막히는 부분 없이 순조로운 진행 ✅
  • 각 단계별 명확한 검증 ✅
  • 미래 문제 미리 예방 ✅
  • 새로운 기술 학습 효과 ✅

🎓 체계적 AI 협업으로 얻은 인사이트들

1. 문제 정의의 중요성

  • 구현 방법보다 "무엇을 구현할지" 명확히 하기
  • 요구사항이 없는 사이드 프로젝트에서도 체계적 접근 가능

2. OAuth 플로우 완전 이해의 가치

  • 단순히 "로그인 구현"이 아닌 각 단계별 보안 이해
  • 질문을 통한 깊이 있는 학습
  • 내 아키텍처에 맞는 커스터마이징

3. 보안은 설계 단계부터

  • "나중에 보안 추가"는 불가능
  • 작동하는 코드 ≠ 안전한 코드
  • 아키텍처 레벨에서 보안 고려

4. AI의 올바른 활용법

  • 단순한 코드 생성기가 아닌 사고 파트너
  • "ultrathink 해주세요" 명시적 요청으로 심층 분석 활용
  • plan 모드로 실행 전 충분한 검토

5. 바이브 코딩의 진화

  • 전통적 바이브 코딩: 직감적이지만 표면적
  • 의식적 체계적 접근: 느리지만 깊이 있고 안전함
  • 계속해서 맹점을 지적하도록 질문하는 습관

6. 업계 표준의 가치

  • VS Code, Discord 같은 성공 사례 분석
  • 바퀴를 다시 발명하지 않기
  • 검증된 패턴 적극 활용

마무리: 체계적 AI 협업을 시작하는 개발자들에게

🚀 의식적 개발 접근법 시작 가이드

1. 구현하기 전에 질문하세요

❌ "Electron OAuth 코드 작성해줘"
✅ "Electron에서 OAuth 구현 시 보안 고려사항을 ultrathink해서 분석해줘"

2. OAuth 같은 복잡한 플로우는 단계별로 이해하세요

"구글이 정확히 뭘 어디로 보내는 거죠?"
"이 단계에서 보안 위험은 없나요?"
"내 아키텍처에서는 어떻게 적용되나요?"

3. 맹점 찾기를 습관화하세요

"이 방법의 문제점이 뭐죠?"
"놓치고 있는 보안 이슈가 있을까요?"  
"업계에서는 이 문제를 어떻게 해결하고 있나요?"

4. Plan 모드를 적극 활용하세요

"복잡한 변경 전에는 Shift+Tab 두 번으로 plan 모드 활성화"
"실행 전 충분한 검토와 분석"
"아키텍처 관점에서 사고하기"

💪 혼자서도 높은 품질의 코드를

체계적 AI 협업의 가장 큰 장점혼자 하는 사이드 프로젝트에서도 팀 개발 수준의 품질을 달성할 수 있다는 점입니다.

  • 동료 개발자의 코드 리뷰 → AI의 맹점 지적
  • 시니어 개발자의 아키텍처 조언 → AI의 업계 표준 분석
  • 보안 전문가의 검토 → AI의 보안 위험 분석

🎯 마지막 조언

OAuth 구현은 단순한 인증 기능이 아닙니다. 사용자의 개인정보를 다루는 핵심 보안 기능입니다.

체계적 AI 협업으로 개발할 때 기억할 것들:

  1. 질문을 멈추지 마세요: "작동하네?"에서 끝나지 말고 계속 질문
  2. 플로우를 완전히 이해하세요: OAuth 각 단계별 의미와 보안 이슈 파악
  3. 보안을 우선하세요: 빠른 구현보다 안전한 구현
  4. 전체를 보세요: 부분적 해결이 아닌 아키텍처 차원의 해결
  5. AI를 잘 활용하세요: plan 모드와 ultrathink로 심층 분석 요청
  6. 바이브 코딩의 함정을 피하세요: 직감보다는 체계적 분석을 우선

무엇보다, 혼자 개발한다고 해서 품질을 타협하지 마세요. 의식적이고 체계적인 AI 협업으로 얼마든지 높은 품질의 코드를 만들 수 있습니다.

안전하고 즐거운 코딩 되세요! 🔒✨


이 프로젝트의 전체 구현 과정과 코드는 GitHub 레포지토리에서 확인하실 수 있습니다.

바이브 코딩 리소스

💡 ultrathink 토큰 할당 정보: "think" (4,000 토큰) < "megathink" (10,000 토큰) < "ultrathink" (31,999 토큰) 순으로 점진적으로 더 많은 사고 자원을 할당받습니다.

profile
늘 성장하고 싶은 개발자입니다 :) 제가 볼려고 쓰는 블로그입니다.

0개의 댓글