dd/LOGIN_BUG_FIX.md

198 lines
5.0 KiB
Markdown

# 🐛 로그인 버그 수정 완료
## 문제 상황
- ✅ 회원가입 후 바로 로그인 → **정상 작동**
- ❌ 로그아웃 후 다시 로그인 시도 → **"이메일 또는 비밀번호가 올바르지 않습니다" 에러**
- 입력한 정보는 모두 정확함
## 🔍 원인 분석
### 문제의 원인
Drizzle ORM 스키마와 코드 간 필드명 불일치
**Drizzle 스키마 (schema.ts):**
```typescript
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) - 수정 전:**
```typescript
const validPassword = await verifyPassword(password, user.password_hash);
// ^^^^^^^^^^^^^ snake_case 사용
```
### 왜 회원가입은 작동했나?
회원가입 시에는 **새로운 passwordHash 값을 직접 전달**하기 때문에 필드명 불일치가 문제가 되지 않았습니다:
```typescript
// register/+page.server.ts
const user = await createUser(platform.env.DB, email, passwordHash, nickname);
// passwordHash를 직접 전달하므로 문제 없음
```
하지만 로그인 시에는 **DB에서 조회한 user 객체의 필드에 접근**하므로:
```typescript
// 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와 비교하므로 항상 실패!
```
## ✅ 해결 방법
### 수정된 코드
```typescript
// src/routes/login/+page.server.ts (수정 후)
const validPassword = await verifyPassword(password, user.passwordHash);
// ^^^^^^^^^^^^ camelCase로 수정
```
## 🔧 수정 내역
**파일:** `src/routes/login/+page.server.ts`
**변경 사항:**
```diff
- 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)
```sql
CREATE TABLE users (
id INTEGER PRIMARY KEY,
password_hash TEXT NOT NULL -- DB는 snake_case
);
```
### TypeScript 타입 (camelCase)
```typescript
// Drizzle가 자동으로 camelCase로 변환
type User = {
id: number;
passwordHash: string; // TypeScript는 camelCase
}
```
### 올바른 사용법
```typescript
// ✅ 올바른 방법
const user = await getUserByEmail(db, email);
console.log(user.passwordHash); // camelCase
// ❌ 잘못된 방법
console.log(user.password_hash); // undefined!
```
## 📝 추가 확인 사항
다른 파일들도 확인했지만 모두 올바르게 작성되어 있습니다:
### ✅ hooks.server.ts
```typescript
event.locals.user = {
id: user.id, // ✅ 올바름
email: user.email, // ✅ 올바름
nickname: user.nickname // ✅ 올바름
};
```
### ✅ db.ts
```typescript
const newUser: NewUser = {
email,
passwordHash, // ✅ camelCase 사용
nickname
};
```
## 🚀 실행 방법
수정사항이 이미 빌드되었으므로 바로 실행 가능합니다:
```bash
pnpm cf:dev
```
그런 다음:
1. http://localhost:8787/register - 새 계정 생성
2. 로그아웃
3. http://localhost:8787/login - 재로그인
4. ✅ 정상 작동 확인!
## 🎓 교훈
### Drizzle ORM 사용 시 주의사항
1. **스키마 정의 시 camelCase 사용**
```typescript
passwordHash: text('password_hash') // TypeScript는 camelCase
```
2. **코드에서도 camelCase로 접근**
```typescript
user.passwordHash // ✅ 올바름
user.password_hash // ❌ undefined
```
3. **TypeScript 타입 활용**
```typescript
// 타입 추론을 사용하면 오타 방지
const hash: string = user.passwordHash;
```
## 🎉 완료!
로그인 버그가 수정되었습니다!
**수정 사항:**
- ✅ `user.password_hash` → `user.passwordHash`로 변경
- ✅ 빌드 성공
- ✅ 테스트 준비 완료
**이제 정상 작동합니다:**
- ✅ 회원가입 → 자동 로그인
- ✅ 로그아웃 → 재로그인
- ✅ 비밀번호 검증 정상 작동
즐거운 개발 되세요! 🚀