redis 제거

This commit is contained in:
2025-12-15 10:15:28 +09:00
parent 2d06f2fcbc
commit d768b8dcef
10 changed files with 205 additions and 228 deletions

View File

@@ -6,20 +6,15 @@ import { VerificationService } from './verification.service';
*
* @description
* 인증번호 생성 및 검증 기능을 제공하는 모듈입니다.
* Redis를 사용하여 인증번호를 임시 저장하고 검증합니다.
* 메모리 Map을 사용하여 인증번호를 임시 저장하고 검증합니다.
*
* 사용 예:
* - 아이디 찾기 인증번호 발송
* - 비밀번호 재설정 인증번호 발송
* - 회원가입 이메일 인증
*
* RedisModule이 @Global로 설정되어 있어 자동으로 주입됩니다.
*
* @export
* @class VerificationModule
*/
@Module({
providers: [VerificationService],
exports: [VerificationService],
providers: [VerificationService],
exports: [VerificationService],
})
export class VerificationModule {}

View File

@@ -1,32 +1,40 @@
import { Injectable, Logger } from '@nestjs/common';
import { InjectRedis } from '@nestjs-modules/ioredis';
import Redis from 'ioredis';
import * as crypto from 'crypto';
import { VERIFICATION_CONFIG } from 'src/common/config/VerificationConfig';
/**
* 인증번호 생성 및 검증 서비스 (Redis 기반)
* 인증번호 생성 및 검증 서비스 (메모리 기반)
*/
@Injectable()
export class VerificationService {
private readonly logger = new Logger(VerificationService.name);
// 메모리 저장소 (key -> { code, expiresAt })
private readonly store = new Map<string, { value: string; expiresAt: number }>();
constructor(@InjectRedis() private readonly redis: Redis) {
this.logger.log(`[REDIS] VerificationService 초기화`);
process.stdout.write(`[REDIS] VerificationService initialized\n`);
constructor() {
this.logger.log(`[VERIFY] VerificationService 초기화 (메모리 모드)`);
// Redis 연결 상태 로깅
this.checkRedisConnection();
// 만료된 항목 정리 (1분마다)
setInterval(() => this.cleanup(), 60000);
}
private async checkRedisConnection(): Promise<void> {
try {
const pong = await this.redis.ping();
this.logger.log(`[REDIS] 연결 상태: ${pong}`);
process.stdout.write(`[REDIS] Connection status: ${pong}\n`);
} catch (error) {
this.logger.error(`[REDIS] 연결 실패: ${error.message}`);
process.stdout.write(`[REDIS] Connection FAILED: ${error.message}\n`);
/**
* 만료된 항목 정리
*/
private cleanup(): void {
const now = Date.now();
let cleaned = 0;
for (const [key, data] of this.store.entries()) {
if (data.expiresAt < now) {
this.store.delete(key);
cleaned++;
}
}
if (cleaned > 0) {
this.logger.debug(`[VERIFY] 만료된 ${cleaned}개 항목 정리됨`);
}
}
@@ -35,109 +43,89 @@ export class VerificationService {
*/
generateCode(): string {
const code = Math.floor(100000 + Math.random() * 900000).toString();
this.logger.log(`[REDIS] 인증번호 생성: ${code}`);
this.logger.log(`[VERIFY] 인증번호 생성: ${code}`);
return code;
}
/**
* 인증번호 저장 (Redis)
* 인증번호 저장 (TTL 적용)
*/
async saveCode(key: string, code: string): Promise<void> {
this.logger.log(`[REDIS] ========== 코드 저장 시작 ==========`);
this.logger.log(`[REDIS] Key: ${key}`);
this.logger.log(`[REDIS] Code: ${code}`);
this.logger.log(`[REDIS] TTL: ${VERIFICATION_CONFIG.CODE_EXPIRY_SECONDS}`);
process.stdout.write(`[REDIS] Saving - Key: ${key}, Code: ${code}\n`);
try {
const result = await this.redis.set(key, code, 'EX', VERIFICATION_CONFIG.CODE_EXPIRY_SECONDS);
this.logger.log(`[REDIS] 저장 결과: ${result}`);
process.stdout.write(`[REDIS] Save result: ${result}\n`);
} catch (error) {
this.logger.error(`[REDIS] ========== 저장 실패 ==========`);
this.logger.error(`[REDIS] Error: ${error.message}`);
this.logger.error(`[REDIS] Stack: ${error.stack}`);
process.stdout.write(`[REDIS] SAVE ERROR: ${error.message}\n`);
process.stdout.write(`[REDIS] STACK: ${error.stack}\n`);
throw error;
}
const expiresAt = Date.now() + (VERIFICATION_CONFIG.CODE_EXPIRY_SECONDS * 1000);
this.store.set(key, { value: code, expiresAt });
this.logger.log(`[VERIFY] 코드 저장 - Key: ${key}, TTL: ${VERIFICATION_CONFIG.CODE_EXPIRY_SECONDS}`);
}
/**
* 인증번호 검증
*/
async verifyCode(key: string, code: string): Promise<boolean> {
this.logger.log(`[REDIS] ========== 코드 검증 시작 ==========`);
this.logger.log(`[REDIS] Key: ${key}`);
this.logger.log(`[REDIS] Input Code: ${code}`);
process.stdout.write(`[REDIS] Verifying - Key: ${key}, Input: ${code}\n`);
this.logger.log(`[VERIFY] 코드 검증 - Key: ${key}, Input: ${code}`);
try {
const savedCode = await this.redis.get(key);
this.logger.log(`[REDIS] Saved Code: ${savedCode}`);
process.stdout.write(`[REDIS] Saved code: ${savedCode}\n`);
const data = this.store.get(key);
if (!savedCode) {
this.logger.warn(`[REDIS] 저장된 코드 없음 (만료 또는 미발급)`);
return false;
}
if (savedCode !== code) {
this.logger.warn(`[REDIS] 코드 불일치 - Saved: ${savedCode}, Input: ${code}`);
return false;
}
await this.redis.del(key);
this.logger.log(`[REDIS] 검증 성공, 코드 삭제됨`);
return true;
} catch (error) {
this.logger.error(`[REDIS] ========== 검증 실패 ==========`);
this.logger.error(`[REDIS] Error: ${error.message}`);
this.logger.error(`[REDIS] Stack: ${error.stack}`);
process.stdout.write(`[REDIS] VERIFY ERROR: ${error.message}\n`);
throw error;
if (!data) {
this.logger.warn(`[VERIFY] 저장된 코드 없음 (만료 또는 미발급)`);
return false;
}
// 만료 체크
if (data.expiresAt < Date.now()) {
this.logger.warn(`[VERIFY] 코드 만료됨`);
this.store.delete(key);
return false;
}
if (data.value !== code) {
this.logger.warn(`[VERIFY] 코드 불일치 - Saved: ${data.value}, Input: ${code}`);
return false;
}
// 검증 성공 시 삭제 (1회용)
this.store.delete(key);
this.logger.log(`[VERIFY] 검증 성공, 코드 삭제됨`);
return true;
}
/**
* 비밀번호 재설정 토큰 생성 및 저장
*/
async generateResetToken(userId: string): Promise<string> {
this.logger.log(`[REDIS] 리셋 토큰 생성 - userId: ${userId}`);
this.logger.log(`[VERIFY] 리셋 토큰 생성 - userId: ${userId}`);
try {
const token = crypto.randomBytes(VERIFICATION_CONFIG.TOKEN_BYTES_LENGTH).toString('hex');
await this.redis.set(`reset:${token}`, userId, 'EX', VERIFICATION_CONFIG.RESET_TOKEN_EXPIRY_SECONDS);
this.logger.log(`[REDIS] 리셋 토큰 저장 완료`);
return token;
} catch (error) {
this.logger.error(`[REDIS] 리셋 토큰 생성 실패: ${error.message}`);
throw error;
}
const token = crypto.randomBytes(VERIFICATION_CONFIG.TOKEN_BYTES_LENGTH).toString('hex');
const expiresAt = Date.now() + (VERIFICATION_CONFIG.RESET_TOKEN_EXPIRY_SECONDS * 1000);
this.store.set(`reset:${token}`, { value: userId, expiresAt });
this.logger.log(`[VERIFY] 리셋 토큰 저장 완료`);
return token;
}
/**
* 비밀번호 재설정 토큰 검증
*/
async verifyResetToken(token: string): Promise<string | null> {
this.logger.log(`[REDIS] 리셋 토큰 검증`);
this.logger.log(`[VERIFY] 리셋 토큰 검증`);
try {
const userId = await this.redis.get(`reset:${token}`);
const key = `reset:${token}`;
const data = this.store.get(key);
if (!userId) {
this.logger.warn(`[REDIS] 리셋 토큰 없음 또는 만료`);
return null;
}
await this.redis.del(`reset:${token}`);
this.logger.log(`[REDIS] 리셋 토큰 검증 성공 - userId: ${userId}`);
return userId;
} catch (error) {
this.logger.error(`[REDIS] 리셋 토큰 검증 실패: ${error.message}`);
throw error;
if (!data) {
this.logger.warn(`[VERIFY] 리셋 토큰 없음 또는 만료`);
return null;
}
if (data.expiresAt < Date.now()) {
this.logger.warn(`[VERIFY] 리셋 토큰 만료됨`);
this.store.delete(key);
return null;
}
this.store.delete(key);
this.logger.log(`[VERIFY] 리셋 토큰 검증 성공 - userId: ${data.value}`);
return data.value;
}
}