Nodejs

NodeJS 회원가입 기능 구현

_HelloWorld_ 2024. 12. 20. 10:11

테이블 정의 및 데이터베이스 설계

## 사용자 관리 테이블 설정

- [x] **사용자 테이블 (Users)**: 사용자의 기본 정보와 계정 관리를 위한 테이블을 생성합니다.
  - `id`, `email`, `password`, `username`, `profile`, `bio`, `role`, `created_at`, `updated_at` 필드 추가

import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';

export interface UserModel {
  id: number;
  username: string;
  email: string;
  password: string;
  bio: string;
  profile: string;
  role: number;
  createdAt: Date;
  updatedAt: Date;
}

@Entity('users')
export class User {
  @PrimaryGeneratedColumn('increment')
  id!: number;

  @Column({ type: 'varchar', length: 100 })
  username!: string;

  @Column({ type: 'varchar', length: 200, unique: true })
  email!: string;

  @Column({ type: 'varchar', length: 255 })
  password!: string;

  @Column({ type: 'varchar', length: 255, nullable: true })
  bio!: string;

  @Column({ type: 'varchar', length: 255, nullable: true })
  profile!: string;

  // 권한 ( 0: 일반 사용자, 1: 관리자 )
  @Column({ type: 'int2', default: 0 })
  role!: number;

  @CreateDateColumn()
  createdAt!: Date;

  @UpdateDateColumn()
  updatedAt!: Date;
}

Bcrypt

import bcrypt from 'bcryptjs';

// 비밀번호 암호화
export const hashPassword = ({password}:{password: string}) => {
  const salt = bcrypt.genSaltSync(10);  // salt 생성
  return bcrypt.hashSync(password, salt);  // 암호화된 비밀번호 반환
};

// 비밀번호 비교
export const comparePassword = ({password, hashedPassword}:{password: string, hashedPassword: string}) => {
  return bcrypt.compareSync(password, hashedPassword);
};

 


Multer 프로필 이미지 업로드

// 이미지 파일 처리 미들웨어
const imageStorage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, path.join(__dirname, '../media/user_profile'));
  },
  filename: (req, file, cb) => {
    const timestamp = Date.now();
    const uniqueName = `${timestamp}-${file.originalname}`;
    cb(null, uniqueName);
  }
});


# multer 설정
export const uploadImage = multer({
  storage: imageStorage,
  fileFilter: imageFilter,
  limits: { fileSize: 5 * 1024 * 1024 } // 예: 이미지 파일 크기 제한 5MB
});
  • destination : 업로드된 파일을 저장할 폴더/디렉토리 정의
  • filename : 저장될 파일의 이름을 설정해줌

파일 저장 흐름

  1. 클라이언트가 이미지 파일을 업로드하면, 이 미들웨어가 호출
  2. destination 콜백을 통해 파일이 저장될 디렉터리가 지정
  3. filename 콜백을 통해 파일 이름이 설정
  4. 설정된 파일 경로와 이름으로 서버 디스크에 저장

API 설계

Controller

// @route put /users/
// @body { username, email, password, profile } 
export const signUp = async (req: Request, res: Response) => {
  try {
    const { username, email, password } = req.body;
    const profile = req.file?.filename;
    const createdUser = await signUpService({username, email, password, profile});
    responseSuccess(res, createdUser, 'User created successfully', 201);
    return
  } catch (error) {
    if (error instanceof Error) {
      responseError(res, error.message, 'Failed to create user', 400);
      return
    }
    responseError(res, 'Internal Server Error', 'Failed to create user', 500);
    return
  }
}
  • 사용자 회원가입 시에 프로필 이미지가 있을 수 있고 없을 수 있는데 이를 Service Props에서 NULL이 가능한 상태로 허용하고 프로필 이미지 데이터가 있다면 DB에 저장하는 방식을 사용

Service

export const signUp = async (signUpProps : SignUpProps) => {
  try {
    const existingUser = await userRepository.findOne({
      where: [
        { username: signUpProps.username },
        { email: signUpProps.email }
      ]
    });
    if (existingUser) {
      throw new Error('User already exists');
    }
    // Hash password
    const hashedPassword = hashPassword({password: signUpProps.password});
    // Create new user
    const newUser = userRepository.create({
      ...signUpProps,
      password: hashedPassword
    })
    // Save user to database
    return await userRepository.save(newUser);
  } catch(error) {
    if (error instanceof Error) {
      throw new Error(error.message);
    }
    throw new Error('Internal Server Error');
  }
}
  • 사용자 회원가입 시에는 동일한 username 혹은 email로 가입한 계정이 있다면 동일한 사용자가 있다는 문구로 400 반환
  • 비밀번호를 데이터베이스에 저장 시에는 평문으로 저장하면 보안에 대한 고려사항이 없다는 것과 동일하기에 bcrypt를 사용하여 비밀번호 암호화

Route

import express from 'express';

// Utils
import { uploadImage } from '../utils/multer';

// Controller
import { signUp } from '../controllers/userController';

const router = express.Router();

// signUp
router.post('/', uploadImage.single('profile'), signUp);

export default router;
  • 회원가입 시에 사용자가 프로필 이미지를 업로드 할 수 있도록 Multer 미들웨어 추가

핵심 요약 

  1. typeORM을 활용하여 데이터베이스 설계 ( 자동 마이그레이션 )
  2. 사용자 회원가입 시에 / 프로필 이미지를 Multer로 처리 ( 이미지가 있을 시에 특정 폴더에 저장하는 방식 ) 
  3. 사용자 회원가입 시에 / 사용자가 입력한 데이터로 / 회원가입을 했었던 / 다른 사용자가 있는 지 확인 

'Nodejs' 카테고리의 다른 글

NodeJS 로그아웃 기능 구현  (1) 2024.12.20
NodeJS 로그인 기능 구현  (0) 2024.12.20
Postgresql TypeORM으로 마이그레이션 하기  (0) 2024.12.19
Nodejs PostgreSQL 연동  (1) 2024.12.18
JWT, Bcrypt 예제  (0) 2024.12.18