dd/LOGIN_BUG_FIX.md

5.0 KiB

🐛 로그인 버그 수정 완료

문제 상황

  • 회원가입 후 바로 로그인 → 정상 작동
  • 로그아웃 후 다시 로그인 시도 → "이메일 또는 비밀번호가 올바르지 않습니다" 에러
  • 입력한 정보는 모두 정확함

🔍 원인 분석

문제의 원인

Drizzle ORM 스키마와 코드 간 필드명 불일치

Drizzle 스키마 (schema.ts):

export const users = sqliteTable('users', {
  id: integer('id').primaryKey({ autoIncrement: true }),
  email: text('email').notNull().unique(),
  passwordHash: text('password_hash').notNull(),  // ← camelCase
  nickname: text('nickname').notNull(),
  createdAt: integer('created_at', { mode: 'timestamp' })
});

로그인 코드 (login/+page.server.ts) - 수정 전:

const validPassword = await verifyPassword(password, user.password_hash);
//                                                         ^^^^^^^^^^^^^ snake_case 사용

왜 회원가입은 작동했나?

회원가입 시에는 새로운 passwordHash 값을 직접 전달하기 때문에 필드명 불일치가 문제가 되지 않았습니다:

// register/+page.server.ts
const user = await createUser(platform.env.DB, email, passwordHash, nickname);
// passwordHash를 직접 전달하므로 문제 없음

하지만 로그인 시에는 DB에서 조회한 user 객체의 필드에 접근하므로:

// login/+page.server.ts
const user = await getUserByEmail(platform.env.DB, email);
// user.passwordHash가 올바른 필드명인데
// user.password_hash로 접근 → undefined 반환
const validPassword = await verifyPassword(password, undefined);
// undefined와 비교하므로 항상 실패!

해결 방법

수정된 코드

// src/routes/login/+page.server.ts (수정 후)
const validPassword = await verifyPassword(password, user.passwordHash);
//                                                         ^^^^^^^^^^^^ camelCase로 수정

🔧 수정 내역

파일: src/routes/login/+page.server.ts

변경 사항:

- const validPassword = await verifyPassword(password, user.password_hash);
+ const validPassword = await verifyPassword(password, user.passwordHash);

테스트 시나리오

이제 다음 시나리오가 모두 정상 작동합니다:

1. 회원가입 → 자동 로그인

1. /register 페이지 접속
2. 정보 입력 (예: test@test.com / 123456 / 테스터)
3. 회원가입 버튼 클릭
4. ✅ 자동으로 로그인되어 메인 페이지로 이동

2. 로그아웃 → 재로그인

1. 로그아웃 버튼 클릭
2. /login 페이지로 이동
3. 동일한 정보 입력 (test@test.com / 123456)
4. 로그인 버튼 클릭
5. ✅ 정상 로그인되어 메인 페이지로 이동

🎯 Drizzle ORM 필드명 규칙

Drizzle ORM은 다음과 같이 필드명을 처리합니다:

데이터베이스 컬럼 (snake_case)

CREATE TABLE users (
  id INTEGER PRIMARY KEY,
  password_hash TEXT NOT NULL  -- DB는 snake_case
);

TypeScript 타입 (camelCase)

// Drizzle가 자동으로 camelCase로 변환
type User = {
  id: number;
  passwordHash: string;  // TypeScript는 camelCase
}

올바른 사용법

// ✅ 올바른 방법
const user = await getUserByEmail(db, email);
console.log(user.passwordHash);  // camelCase

// ❌ 잘못된 방법
console.log(user.password_hash);  // undefined!

📝 추가 확인 사항

다른 파일들도 확인했지만 모두 올바르게 작성되어 있습니다:

hooks.server.ts

event.locals.user = {
  id: user.id,           // ✅ 올바름
  email: user.email,     // ✅ 올바름
  nickname: user.nickname // ✅ 올바름
};

db.ts

const newUser: NewUser = {
  email,
  passwordHash,  // ✅ camelCase 사용
  nickname
};

🚀 실행 방법

수정사항이 이미 빌드되었으므로 바로 실행 가능합니다:

pnpm cf:dev

그런 다음:

  1. http://localhost:8787/register - 새 계정 생성
  2. 로그아웃
  3. http://localhost:8787/login - 재로그인
  4. 정상 작동 확인!

🎓 교훈

Drizzle ORM 사용 시 주의사항

  1. 스키마 정의 시 camelCase 사용

    passwordHash: text('password_hash')  // TypeScript는 camelCase
    
  2. 코드에서도 camelCase로 접근

    user.passwordHash  // ✅ 올바름
    user.password_hash // ❌ undefined
    
  3. TypeScript 타입 활용

    // 타입 추론을 사용하면 오타 방지
    const hash: string = user.passwordHash;
    

🎉 완료!

로그인 버그가 수정되었습니다!

수정 사항:

  • user.password_hashuser.passwordHash로 변경
  • 빌드 성공
  • 테스트 준비 완료

이제 정상 작동합니다:

  • 회원가입 → 자동 로그인
  • 로그아웃 → 재로그인
  • 비밀번호 검증 정상 작동

즐거운 개발 되세요! 🚀