백엔드 개발/인증 · 보안

[Node.js] 중복 로그인 어떻게 막을까? JWT + Redis 실무 구현 예시

JeongPark 2023. 7. 5. 22:31
728x90

[Node.js] 중복 로그인 처리하기 – 실무에서 세션 제어하는 방법

서비스를 운영하다 보면 "동일 계정으로 동시에 여러 기기에서 로그인"되는 문제를 제어하고 싶을 때가 있습니다.
보안이 중요한 서비스라면 중복 로그인 방지 처리가 필수죠.


목표

  • 한 계정으로 로그인했을 때, 기존 세션이 존재하면 처리 방식 결정
    • 이전 세션 강제 만료
    • 중복 로그인 차단
  • Redis + JWT + Node.js로 구현

전제 조건

  • JWT 기반 인증 (accessToken, refreshToken)
  • 로그인 시 Redis에 refreshToken 저장
  • 인증 시 Redis 내 토큰과 일치 여부 검증

예시 코드 – 로그인 시 토큰 발급 및 중복 확인

// 로그인 서비스 예시
import { getRefreshToken, saveRefreshToken } from './redis';

export const loginUser = async (email: string, password: string) => {
  const user = await userRepository.findOne({ where: { email } });
  if (!user) throw new Error('존재하지 않는 사용자입니다.');

  const isMatch = await bcrypt.compare(password, user.password);
  if (!isMatch) throw new Error('비밀번호가 일치하지 않습니다.');

  const refreshToken = generateRefreshToken(user.id); // 새 토큰 발급

  const existing = await getRefreshToken(user.id);
  if (existing) {
    // 이미 로그인된 사용자 (중복 로그인 처리)
    // 방법 1: 기존 토큰 삭제 → 새로운 세션만 유지
    await saveRefreshToken(user.id, refreshToken);

    // 방법 2: 에러 반환 → 중복 로그인 차단
    // throw new Error('이미 로그인된 세션이 존재합니다.');
  } else {
    await saveRefreshToken(user.id, refreshToken);
  }

  const accessToken = generateAccessToken(user.id);
  return { accessToken, refreshToken };
};

중복 로그인 처리 전략

전략 설명
기존 세션 무효화 새 로그인 시 이전 세션(리프레시 토큰) 삭제
중복 로그인 차단 이미 존재하면 새 로그인 거부
병행 허용 여러 토큰 저장 (추가 구현 필요)

Redis에 저장 구조 예시

// 키: userId, 값: refreshToken
await redis.set(`refresh:${userId}`, token, { EX: 60 * 60 * 24 * 7 }); // 7일 만료

실무 팁

  • 단말별 로그인 허용이 필요하면 deviceId 기반 구조로 확장
  • 로그아웃 시에도 Redis에서 토큰 삭제 필수
  • 강제 로그아웃 시 WebSocket, SSE 연동 가능

결론

  • 중복 로그인 처리 여부는 서비스 정책에 따라 다르게 설계해야 함
  • Redis + JWT 조합으로 간단하게 구현 가능
  • 확장이 필요하면 device-level, IP, location 등을 고려해 추가 설계 가능
반응형