본문 바로가기
IT 프로젝트 공모전(백엔드)

[Spring boot] 회원가입 기능구현 1 (설계, 계층, ERD, SQL 스키마)

by CromArchive 2025. 7. 15.
반응형

이번 Tema-IT 프로젝트에서 백엔드로서 가장 먼저 구상 및 구현한 기능은 회원가입 기능이다.

예전에 진행한 플러터를 사용했던 앱 프로젝트와는 다르게, DB 구축부터 연결, 회원가입 로직을 대부분 구현했다는 것이 차이점이다.

 

회원가입 흐름

원래 초기 기획했었던 회원가입 및 로그인 흐름은 아래와 같다.

회원가입 정보를 받고 DB에서 중복 검사를 한 다음 이메일 인증을 거쳐서 통과하면 회원가입 성공 표시를 띄우는 방식이었다.

그러나 현재 프로젝트에서 이메일 인증 기능은 삭제되었다.

왜냐하면 우리 프로젝트에서는 uid(User ID)가 일종의 이메일과 같은 역할을 한다.

uid는 고유값을 갖고 있고 내부 알림함도 존재하기 때문에 이메일을 사용해서 굳이 외부로 연락을 취할 이유가 없다는 것이 주된 이유였다.

그래서 현재 회원가입 절차는 기본적인 회원 정보를 입력받고 입력된 정보 중 uid와 이메일 정보가 중복인지만 한번 검증하는 형태이다.

임시 UI를 보면 아래와 같다.


회원가입 백엔드 계층 역할 알아보기

이번 프로젝트는 Spring boot 프레임워크를 사용하였다.

회원가입요청을 처리하는 순서는 다음과 같다.

Controller → DTO → Service → Repository → JPA → DB → JPA → Service → Controller → Response

여기서 각 계층의 역할을 먼저 알아보자.

 

Controller

  • Http 요청을 받아오는 진입점
  • URL 매핑,인증,권한 검사 후 Service 호출
  • Service 반환값 수신
  • Response DTO로 매핑 후 HTTP 상태코드 설정

 

DTO (Request DTO)

  • JSON → java 객체 바인딩 (두 객체를 묶어 데이터 소스를 일치시키는 것)
  • @Valid 애노테이션으로 입력값 검증

 

Service(비즈니스 로직 / 결과 후처리)

  • 트랜잭션 관리 (한번에 처리되는 일련의 작업 단위 - 중간에 하나라도 실패하면 전체가 실패)
  • 중복 검사, 비밀번호 암호화 등 도메인 규칙 적용
  • Repository 호출로 데이터 저장 조회
  • 리턴된 Entity를 비즈니스 로직에 맞게 가공
  • Response DTO 변환 준비

 

Repository(데이터 접근 계층)

  • JapRepository 인터페이스로 CRUD 메서드 제공
  • Entity 객체를 영속성 컨텍스트(엔티티를 영구 저장하는 환경)에 등록

 

JAP(쿼리 생성 및 전송 / 결과 매핑)

  • Repository 호출을 기반으로 SQL 생성
  • JDBC 커넥션을 통해 DB로 쿼리 전송
  • DB응답을 받아와 Entity 객체로 변환
  • 영속성 컨텍스트에 관리

 

DB

  • 실제 SQL 실행
  • 테이블에 데이터 삽입-조회-수정-삭제 작업

 

Response(Response DTO + Wrapper)

  • 클라이언트에 돌아가는 최종 JSON 포맷

DB ERD 작성하기

DB를 구성하기 위해서는 먼저 ERD가 필요하다.

어떤 정보를 테이블로 묶을지, 어떤 타입으로 저장할지 등을 추상적으로 작성하는 과정이다.

ERD는 이런식으로 작성하였고 1:N으로 다중매핑되는 경우 별도의 테이블로 분리 후 매핑해주었다.


SQL 스키마 작성하기

ERD를 다 작성하고 DB를 사용하기 위한 SQL스키마를 작성하였다.

초기에는 이렇게 작성하였다.

	-- 사용자 테이블
	CREATE TABLE `users` (
	  `uid`            VARCHAR(50)   NOT NULL,
	  `pw`             VARCHAR(255)  NOT NULL,
	  `nick_name`      VARCHAR(100)  NOT NULL,
	  `email`          VARCHAR(255)  NOT NULL,
	  `email_verified` TINYINT(1)    NOT NULL DEFAULT 0,
	  `birthday`       INT           NOT NULL,
	  `point`          BIGINT        NOT NULL DEFAULT 0,
	  `profile_img`    VARCHAR(2083),
	  `created_at`     DATETIME      NOT NULL DEFAULT CURRENT_TIMESTAMP,
	  `my_score`       DOUBLE,
	  PRIMARY KEY (`uid`),
	  UNIQUE KEY `uq_users_email`     (`email`),
	  UNIQUE KEY `uq_users_nickname`  (`nick_name`)
	) ENGINE=InnoDB
	  DEFAULT CHARSET=utf8mb4
	  COLLATE=utf8mb4_unicode_ci;
	
	-- 조직 테이블
	CREATE TABLE `organization_table` (
	  `id`         INT          NOT NULL AUTO_INCREMENT,
	  `organ_name` VARCHAR(255) NOT NULL,
	  `organ_icon` VARCHAR(2083),
	  PRIMARY KEY (`id`),
	  UNIQUE KEY `uq_organ_name` (`organ_name`)
	) ENGINE=InnoDB
	  DEFAULT CHARSET=utf8mb4
	  COLLATE=utf8mb4_unicode_ci;
	
	-- 스택 테이블
	CREATE TABLE `stack_table` (
	  `id`        INT         NOT NULL AUTO_INCREMENT,
	  `stack_tag` VARCHAR(100) NOT NULL,
	  `stack_icon`VARCHAR(2083),
	  PRIMARY KEY (`id`),
	  UNIQUE KEY `uq_stack_tag` (`stack_tag`)
	) ENGINE=InnoDB
	  DEFAULT CHARSET=utf8mb4
	  COLLATE=utf8mb4_unicode_ci;
	
	-- 직군 테이블
	CREATE TABLE `position_table` (
	  `id`           INT         NOT NULL AUTO_INCREMENT,
	  `position_tag` VARCHAR(100) NOT NULL,
	  `position_icon`VARCHAR(2083),
	  PRIMARY KEY (`id`),
	  UNIQUE KEY `uq_position_tag` (`position_tag`)
	) ENGINE=InnoDB
	  DEFAULT CHARSET=utf8mb4
	  COLLATE=utf8mb4_unicode_ci;
	
	-- 사용자-조직 매핑
	CREATE TABLE `user_organizations` (
	  `uid`             VARCHAR(50) NOT NULL,
	  `organization_id` INT         NOT NULL,
	  PRIMARY KEY (`uid`,`organization_id`),
	  FOREIGN KEY (`uid`)             REFERENCES `users`           (`uid`) ON DELETE CASCADE,
	  FOREIGN KEY (`organization_id`) REFERENCES `organization_table`(`id`)  ON DELETE CASCADE
	) ENGINE=InnoDB
	  DEFAULT CHARSET=utf8mb4
	  COLLATE=utf8mb4_unicode_ci;
	
	-- 사용자-스택 매핑
	CREATE TABLE `user_stacks` (
	  `uid`     VARCHAR(50) NOT NULL,
	  `stack_id`INT         NOT NULL,
	  PRIMARY KEY (`uid`,`stack_id`),
	  FOREIGN KEY (`uid`)      REFERENCES `users`       (`uid`) ON DELETE CASCADE,
	  FOREIGN KEY (`stack_id`) REFERENCES `stack_table` (`id`)  ON DELETE CASCADE
	) ENGINE=InnoDB
	  DEFAULT CHARSET=utf8mb4
	  COLLATE=utf8mb4_unicode_ci;
	
	-- 사용자-직군 매핑
	CREATE TABLE `user_positions` (
	  `uid`         VARCHAR(50) NOT NULL,
	  `position_id` INT         NOT NULL,
	  PRIMARY KEY (`uid`,`position_id`),
	  FOREIGN KEY (`uid`)         REFERENCES `users`          (`uid`) ON DELETE CASCADE,
	  FOREIGN KEY (`position_id`) REFERENCES `position_table` (`id`)  ON DELETE CASCADE
	) ENGINE=InnoDB
	  DEFAULT CHARSET=utf8mb4
	  COLLATE=utf8mb4_unicode_ci;

추후 관리자 및 유저 권한을 분리하기 위해 아래 스키마를 추가해주었다.

// 관리자 계정을 위해서 추가
ALTER TABLE users
  ADD COLUMN roles VARCHAR(50) NOT NULL DEFAULT 'ROLE_USER';

그 다음 데이터 업데이트 시각을 저장해놓는 컬럼이 빠졌다는 것을 깨닫고 아래 스키마도 추가해주었다.

// 유저 업데이트 시각 열람용
ALTER TABLE `users`
  ADD COLUMN `updated_at` DATETIME NOT NULL
    DEFAULT CURRENT_TIMESTAMP
    ON UPDATE CURRENT_TIMESTAMP;

이제 이걸 실제 DB에 반영하면 아래처럼 만들 수 있다.

 

728x90
반응형