[LG CNS AM INSPIRE 6기] 26일차 TIL: Spring Boot + MyBatis 회원가입·로그인과 JWT 구조

펭귄's 다이어리·2026년 9월 3일
post-thumbnail

오늘의 한 줄 요약

25일차에 학습한 Spring Boot + MyBatis 기본 계층 구조를 바탕으로 회원가입과 로그인 기능을 구현하고 CORS, JWT, HTTP Header, 환경변수(.env), Profile(dev/prod)까지 적용하며 실제 서비스에 가까운 백엔드 구조로 확장했다.


배운 내용

  • Spring Boot + MyBatis 기반 회원가입 API 구현
  • MyBatis Mapper Interface와 XML Mapper 연결
  • 로그인 기능 구현
  • Optional을 이용한 로그인 결과 처리
  • ResponseEntity를 이용한 HTTP 응답 제어
  • HTTP Status Code 활용
  • HTTP Header를 이용한 Token 전달
  • JWT의 Access Token / Refresh Token 구조
  • React와 Spring 연결을 위한 CORS 설정
  • Swagger를 이용한 회원가입 / 로그인 API 명세
  • .env를 이용한 DB 설정값 외부화
  • application.yml, application-dev.yml, application-prod.yml 분리
  • Spring Profile을 이용한 개발환경과 운영환경 분리
  • 25일차 testcase 프로젝트와 26일차 실제 서비스 구조의 차이

핵심 개념

1. 26일차 프로젝트 구조

이번 프로젝트의 주요 파일 구조는 다음과 같다.

C:\inpire_6th\BE\inspire_mybatis
│
├─ .env
│
└─ src
   └─ main
      ├─ java
      │  └─ com.example.inspire_mybatis
      │
      │     ├─ features
      │     │  ├─ commons
      │     │  │  ├─ config
      │     │  │  │  └─ CorsConfig.java
      │     │  │  │
      │     │  │  └─ token
      │     │  │     └─ JwtProvider.java
      │     │  │
      │     │  └─ users
      │     │     ├─ ctrl
      │     │     │  └─ UserController.java
      │     │     │
      │     │     ├─ domain
      │     │     │  └─ dto
      │     │     │     ├─ UserRequestDTO.java
      │     │     │     └─ UserResponseDTO.java
      │     │     │
      │     │     ├─ repository
      │     │     │  └─ UserMapper.java
      │     │     │
      │     │     └─ service
      │     │        └─ UserService.java
      │     │
      │     └─ InspireMybatisApplication.java
      │
      └─ resources
         ├─ mappers
         │  └─ userMapper.xml
         │
         ├─ application.yml
         ├─ application-dev.yml
         └─ application-prod.yml

전체적인 요청 처리 흐름은 다음과 같다.

React
localhost:3000
     ↓
CorsConfig
     ↓
UserController
     ↓
UserService
     ↓
UserMapper
     ↓
userMapper.xml
     ↓
MariaDB

로그인에서는 JWT 관련 기능이 추가된다.

UserController
      ↓
UserService
      ├──────────────→ UserMapper
      │                   ↓
      │              userMapper.xml
      │                   ↓
      │                 DB
      │
      └──────────────→ JwtProvider
                           ↓
                 Access Token
                 Refresh Token

2. CORS

CORS란?

CORS는 Cross-Origin Resource Sharing의 약자로 서로 다른 Origin 간 HTTP 요청을 허용하기 위한 정책이다.

현재 개발 환경은 다음과 같이 구성되어 있다.

React
http://localhost:3000

Spring Boot
http://localhost:8000

Port가 다르기 때문에 브라우저에서는 서로 다른 Origin으로 판단한다.

이를 허용하기 위해 CorsConfig를 작성했다.

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    
    @Override
    public void addCorsMappings(CorsRegistry registry) {

        registry.addMapping("/**")
            .allowedHeaders("*")
            .allowedOriginPatterns("http://localhost:3000")
            .allowedMethods(
                "GET",
                "POST",
                "DELETE",
                "PUT",
                "PATCH",
                "OPTIONS"
            );
    }
}

주요 설정

registry.addMapping("/**")

모든 URL에 CORS 설정을 적용한다.

.allowedOriginPatterns("http://localhost:3000")

React 개발 서버에서 들어오는 요청을 허용한다.

.allowedHeaders("*")

모든 Request Header를 허용한다.

.allowedMethods(
    "GET",
    "POST",
    "DELETE",
    "PUT",
    "PATCH",
    "OPTIONS"
)

사용 가능한 HTTP Method를 지정한다.


3. UserRequestDTO

사용자가 서버로 전달하는 요청 데이터를 담는 DTO이다.

@Builder
@Getter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class UserRequestDTO {

    private String email;
    private String password;
    private String name;

}

Lombok을 사용하여 Getter, Builder, Constructor 등을 자동 생성했다.

주요 Lombok Annotation

@Getter

Getter 자동 생성

@Builder

Builder Pattern 사용 가능

@NoArgsConstructor

기본 생성자 자동 생성

@AllArgsConstructor

모든 필드를 받는 생성자 자동 생성

@ToString

객체 정보를 문자열로 출력할 수 있다.


4. UserResponseDTO

서버에서 클라이언트에게 전달할 사용자 데이터를 담는다.

@Builder
@Getter
@ToString
public class UserResponseDTO {

    private String email;
    private String password;
    private String name;

}

Request DTO와 Response DTO를 분리하여 사용하는 구조를 학습했다.

Client
   ↓
UserRequestDTO
   ↓
Controller
   ↓
Service
   ↓
DB
   ↓
UserResponseDTO
   ↓
Client

실무에서는 보안상 password를 Response DTO에 포함시키지 않는 것이 일반적이다.


5. UserMapper

MyBatis를 이용하여 DB에 접근하는 Mapper Interface이다.

@Mapper
public interface UserMapper {

    public int save(UserRequestDTO request);

    public Optional<UserResponseDTO> signIn(
        UserRequestDTO request
    );

}

@Mapper

@Mapper

MyBatis가 해당 Interface를 Mapper로 인식하도록 한다.

직접 구현 클래스를 작성하지 않아도 MyBatis가 XML Mapper와 연결하여 실행한다.


MyBatis Interface와 XML 연결

UserMapper.java

public int save(UserRequestDTO request);

userMapper.xml

<insert id="save">

id와 메서드 이름을 동일하게 작성하여 연결한다.

로그인도 동일하다.

public Optional<UserResponseDTO> signIn(
    UserRequestDTO request
);
<select id="signIn">

6. MyBatis namespace

XML Mapper의 가장 중요한 설정 중 하나이다.

<mapper
    namespace=
    "com.example.inspire_mybatis.features.users.repository.UserMapper">

namespace에는 연결할 Mapper Interface의 전체 패키지 경로를 입력한다.

즉,

UserMapper.java
        ↕
userMapper.xml

을 연결해 주는 역할을 한다.


7. 회원가입 SQL

회원가입에서는 INSERT를 사용한다.

<insert
    id="save"
    parameterType=
    "com.example.inspire_mybatis.features.users.domain.dto.UserRequestDTO">

    INSERT INTO SPRING_USER_TBL(
        EMAIL,
        PASSWORD,
        NAME
    )

    VALUES(
        #{email},
        #{password},
        #{name}
    )

</insert>

DTO의 필드는 다음과 같이 SQL에 전달한다.

request.email
    ↓
#{email}

request.password
    ↓
#{password}

request.name
    ↓
#{name}

8. MyBatis #{}

MyBatis에서는 다음과 같이 DTO의 값을 SQL Parameter로 전달할 수 있다.

#{email}

예를 들어

UserRequestDTO request

에 다음 값이 들어있다면,

email = test@test.com
password = 1234
name = TEST

MyBatis에서는 다음 SQL에 값을 Binding한다.

INSERT INTO SPRING_USER_TBL(
    EMAIL,
    PASSWORD,
    NAME
)
VALUES(
    #{email},
    #{password},
    #{name}
);

9. 회원가입 전체 흐름

회원가입 API는 다음과 같다.

POST /users/signUp

전체 처리 과정은

React
  ↓
POST /users/signUp
  ↓
UserController
  ↓
UserService.signUp()
  ↓
UserMapper.save()
  ↓
userMapper.xml
  ↓
INSERT
  ↓
SPRING_USER_TBL

이다.


10. UserController 회원가입

@PostMapping("/signUp")
public ResponseEntity<?> signUp(
    @RequestBody UserRequestDTO request
) {

    int signUpFlag = userService.signUp(request);

    if(signUpFlag != 0) {

        return ResponseEntity
                .status(HttpStatus.CREATED)
                .body(null);

    } else {

        return ResponseEntity
                .status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(null);
    }
}

MyBatis의 INSERT 결과값으로 영향을 받은 Row의 개수를 받을 수 있다.

INSERT 성공
→ 1

INSERT 실패
→ 0

따라서

if(signUpFlag != 0)

로 회원가입 성공 여부를 판단했다.


11. ResponseEntity

Spring에서 HTTP Response 전체를 직접 제어할 때 사용한다.

ResponseEntity

다음을 설정할 수 있다.

HTTP Status
HTTP Header
HTTP Body

예를 들어 회원가입 성공 시

return ResponseEntity
    .status(HttpStatus.CREATED)
    .body(null);

결과는

HTTP/1.1 201 Created

가 된다.


12. HTTP Status Code

이번 실습에서 사용한 주요 Status Code는 다음과 같다.

200 OK
요청 성공

201 Created
리소스 생성 성공

400 Bad Request
잘못된 요청

500 Internal Server Error
서버 내부 오류

회원가입 성공에서는

HttpStatus.CREATED

로그인 성공에서는

HttpStatus.OK

를 사용했다.


로그인

13. 로그인 SQL

로그인은 Email과 Password가 모두 일치하는 사용자를 조회하는 방식으로 구현했다.

<select
    id="signIn"
    parameterType=
    "com.example.inspire_mybatis.features.users.domain.dto.UserRequestDTO"
    resultType=
    "com.example.inspire_mybatis.features.users.domain.dto.UserResponseDTO">

    SELECT
        EMAIL,
        PASSWORD,
        NAME

    FROM
        SPRING_USER_TBL

    WHERE
        EMAIL = #{email}
        AND PASSWORD = #{password}

</select>

14. 로그인 요청 데이터 생성

Controller에서 Email과 Password를 받는다.

@RequestParam("email")
String email,

@RequestParam("password")
String password

그리고 Builder를 사용하여 DTO를 생성한다.

UserRequestDTO.builder()
    .email(email)
    .password(password)
    .build();

즉 다음과 같은 형태다.

email
password
   ↓
UserRequestDTO
   ↓
UserService.signIn()

15. Optional

로그인 Mapper의 반환 타입으로 Optional을 사용했다.

Optional<UserResponseDTO> signIn(
    UserRequestDTO request
);

DB에서 사용자가 조회될 수도 있고 조회되지 않을 수도 있기 때문이다.

사용자 있음
→ Optional<UserResponseDTO>

사용자 없음
→ Optional.empty()

16. orElseThrow()

Service에서는 다음 코드를 사용했다.

UserResponseDTO response =
    userMapper
        .signIn(request)
        .orElseThrow(
            () -> new RuntimeException("로그인 실패")
        );

사용자가 존재하면 UserResponseDTO를 반환한다.

사용자가 존재하지 않으면

RuntimeException

을 발생시킨다.

전체 흐름은

UserMapper.signIn()
       ↓
Optional
       ↓
사용자 존재?
 ┌─────┴─────┐
 YES         NO
 ↓            ↓
DTO       Exception

이다.


JWT

17. 인증(Authentication)과 인가(Authorization)

향후 로그인 기능에서는 다음 개념이 중요하다.

Authentication

사용자가 누구인지 확인하는 과정

"당신이 누구인가?"

예:

Email + Password
→ 사용자 확인

Authorization

로그인한 사용자가 특정 기능을 사용할 권한이 있는지 확인하는 과정

"당신이 이 기능을 사용할 권한이 있는가?"

18. JWT

JWT는

JSON Web Token

의 약자로 사용자 인증 정보를 Token 형태로 전달하는 방식이다.

이번 프로젝트에서는 아직 실제 JWT Token을 생성하는 단계가 아니라 JWT 구조를 먼저 구현했다.

@Component
public class JwtProvider {
    
    public String createAT(String email) {

        return "Bearer XXXXXXX";
    }

    public String createRT(String email) {

        return "xxxxxxxxxxxxxxxx";
    }
}

19. Access Token

AT
=
Access Token

로그인 이후 API 요청 시 사용자의 인증 정보를 확인하기 위해 사용한다.

일반적으로 HTTP Header에 다음과 같이 전달한다.

Authorization: Bearer JWT_TOKEN

20. Refresh Token

RT
=
Refresh Token

Access Token이 만료되었을 때 새로운 Access Token을 발급받기 위해 사용한다.

개념적인 구조는

로그인
  ↓
Access Token
Refresh Token
  ↓
Access Token을 이용하여 API 호출
  ↓
Access Token 만료
  ↓
Refresh Token 확인
  ↓
새로운 Access Token 발급

이다.


21. 로그인 성공 후 Token 생성

Service에서 로그인 성공 후 Token을 발급한다.

String at =
    jwtProvider.createAT(request.getEmail());

String rt =
    jwtProvider.createRT(request.getEmail());

그리고 사용자 정보와 Token을 Map에 저장한다.

Map<String, Object> map =
    new HashMap<>();

map.put("response", response);
map.put("at", at);
map.put("rt", rt);

구조는 다음과 같다.

Map

response
→ UserResponseDTO

at
→ Access Token

rt
→ Refresh Token

22. HttpHeaders

Controller에서는 Token을 HTTP Response Header에 담아 반환한다.

HttpHeaders headers =
    new HttpHeaders();

headers.add(
    "Authorization",
    (String) map.get("at")
);

headers.add(
    "Refresh-Token",
    (String) map.get("rt")
);

클라이언트는 다음과 같은 응답을 받을 수 있다.

HTTP/1.1 200 OK

Authorization: Bearer XXXXXXX
Refresh-Token: xxxxxxxxxxxxxxxx

Body에는 사용자 정보를 반환한다.

.body(
    (UserResponseDTO) map.get("response")
);

23. Access-Control-Expose-Headers

다음 설정도 추가했다.

headers.add(
    "Access-Control-Expose-Headers",
    "Authorization, Refresh-Token"
);

브라우저에서는 기본적으로 모든 Response Header를 JavaScript에서 읽을 수 있는 것은 아니다.

따라서 React에서

Authorization
Refresh-Token

Header를 읽을 수 있도록 해당 Header를 노출시킨다.


환경 설정

24. .env

DB 계정이나 Password와 같은 중요한 설정값을 코드에 직접 작성하지 않고 별도 환경변수 파일에서 관리한다.

현재 .env에는 다음과 같은 내용이 필요하다.

DB_DRIVER=org.mariadb.jdbc.Driver
DB_URL=jdbc:mariadb://localhost:3306/lgcns
DB_USER=root
DB_PASSWORD=본인의_DB_비밀번호

예:

DB_DRIVER=org.mariadb.jdbc.Driver
DB_URL=jdbc:mariadb://localhost:3306/lgcns
DB_USER=root
DB_PASSWORD=1234

25. .env를 사용하는 이유

다음과 같은 정보를 Source Code 내부에 직접 작성하면 보안상 좋지 않다.

DB Password
API KEY
JWT Secret Key
Cloud Access Key

따라서 환경변수로 분리한다.

.env
  ↓
환경변수
  ↓
application-dev.yml
  ↓
Spring Boot

Git을 사용할 경우 .env는 Repository에 올리지 않는 것이 중요하다.

.gitignore

.env

26. Dotenv

.env 설정을 Spring의 설정 파일에서 사용할 수 있도록 애플리케이션 시작 시 환경변수로 등록했다.

Dotenv env =
    Dotenv.configure()
          .ignoreIfMissing()
          .load();

그리고

env.entries().forEach(entry -> {

    System.setProperty(
        entry.getKey(),
        entry.getValue()
    );

});

를 이용하여 .env에 있는 값을 Java System Property에 등록한다.

전체 흐름은

.env
 ↓
Dotenv
 ↓
System.setProperty()
 ↓
${DB_URL}
${DB_USER}
${DB_PASSWORD}
 ↓
application-dev.yml
 ↓
DataSource
 ↓
MariaDB

이다.


Spring Profile

27. application.yml

기본 Profile을 설정한다.

spring:
  profiles:
    default: dev

별도의 Profile을 지정하지 않으면 dev 환경을 사용한다.


28. application-dev.yml

개발환경 설정 파일이다.

server:
  port: 8000

spring:

  config:
    activate:
      on-profile: dev

  datasource:
    driver-class-name: ${DB_DRIVER}
    url: ${DB_URL}
    username: ${DB_USER}
    password: ${DB_PASSWORD}

mybatis:
  mapper-locations: classpath:/mappers/**/*Mapper.xml

29. application-prod.yml

운영환경에서 사용할 설정 파일이다.

spring:

  config:

    activate:

      on-profile: prod

현재는 기본적인 Profile 구조만 생성한 단계이다.

향후에는 개발환경과 실제 운영환경의 DB 설정 등을 각각 다르게 설정할 수 있다.

application.yml

        ↓

 ┌──────────────┐
 │              │
dev            prod
 │              │
 ↓              ↓
개발환경        운영환경

Swagger

30. 회원가입 API 명세화

@Operation(
    summary = "회원가입",
    description = "신규가입(email, password, name)"
)

API 결과 Status도 정의했다.

@ApiResponses({
    @ApiResponse(
        responseCode = "201",
        description = "가입성공"
    ),
    @ApiResponse(
        responseCode = "400",
        description = "유효성검사실패"
    ),
    @ApiResponse(
        responseCode = "500",
        description = "가입실패"
    )
})

31. 로그인 API 명세화

@Operation(
    summary = "로그인",
    description = "사용자 로그인(email, password)"
)
@ApiResponses({
    @ApiResponse(
        responseCode = "200",
        description = "로그인 성공"
    ),
    @ApiResponse(
        responseCode = "400",
        description = "로그인 실패"
    )
})

Swagger는 다음 URL에서 확인할 수 있다.

http://localhost:8000/swagger-ui/index.html

25일차와 26일차의 차이

32. 25일차 testcase

25일차에는 주로 Spring Boot와 MyBatis가 연결되는 기본 구조를 학습했다.

Request
   ↓
Controller
   ↓
Service
   ↓
Mapper
   ↓
MyBatis XML
   ↓
Database

핵심은

Spring MVC
+
MyBatis

의 동작 원리를 이해하는 것이었다.


33. 26일차 inspire_mybatis

26일차에는 25일차 구조를 그대로 활용하면서 실제 서비스 기능을 추가했다.

React
 ↓
CORS
 ↓
Controller
 ↓
Service
 ├────────→ Mapper
 │             ↓
 │         MyBatis XML
 │             ↓
 │             DB
 │
 └────────→ JwtProvider
               ↓
          Access Token
          Refresh Token

즉 단순히 Spring과 MyBatis의 연결 여부를 확인하는 단계에서 실제 웹 서비스의 인증 흐름을 구현하는 단계로 발전했다.


34. 25일차와 26일차 비교

구분25일차 testcase26일차 inspire_mybatis
목적Spring + MyBatis 구조 이해실제 회원 기능 구현
Controller요청 처리 학습회원가입 / 로그인 API
Service기본 계층 연결로그인 검증 및 Token 발급
MapperMyBatis 연결회원 저장 / 로그인 조회
DTO요청 데이터 전달Request / Response 역할 분리
SwaggerAPI 명세 학습실제 회원 API 명세
CORS없음React 연동
JWT없음AT / RT 구조 추가
HTTP Header기본 응답Token 전달
환경변수기본 설정.env 도입
Profile단순 설정dev / prod 환경 분리
프로젝트 성격Test Case실제 서비스 구조

새롭게 알게 된 점

  • MyBatis Mapper Interface와 XML Mapper는 namespace, id를 이용하여 연결된다.
  • INSERT 실행 결과로 영향을 받은 Row의 수를 받을 수 있다.
  • Optional을 이용하면 조회 결과가 존재하지 않는 상황을 명확하게 처리할 수 있다.
  • ResponseEntity를 사용하면 HTTP Status, Header, Body를 모두 직접 제어할 수 있다.
  • 로그인 성공 후 Token을 Response Body가 아닌 HTTP Header로 전달할 수 있다.
  • JWT 인증에는 Access Token과 Refresh Token을 함께 사용하는 방식이 있다.
  • React에서 Spring Response Header를 읽기 위해서는 Access-Control-Expose-Headers 설정이 필요할 수 있다.
  • React와 Spring처럼 서로 다른 Origin을 사용하는 경우 CORS 설정이 필요하다.
  • .env를 이용하면 DB Password 같은 민감한 설정을 Source Code에서 분리할 수 있다.
  • Spring Profile을 이용하여 개발환경과 운영환경의 설정을 분리할 수 있다.
  • 25일차에 배운 Controller → Service → Mapper → DB 구조가 실제 서비스에서도 그대로 사용되고 그 주변에 인증, CORS, 환경설정 등의 기능이 추가된다.

헷갈렸던 점

1. 25일차 프로젝트와 무엇이 다른가?

처음에는 25일차 testcase와 26일차 프로젝트가 구조적으로 크게 달라 보이지 않았다.

하지만 실제로는 기본적인 계층 구조는 동일하다.

Controller
 ↓
Service
 ↓
Mapper
 ↓
DB

차이는 이 구조를 이용하여 실제 서비스 기능을 붙였다는 점이다.

26일차에는

회원가입
로그인
JWT
CORS
HTTP Header
환경변수
Profile

등이 추가되었다.

즉 새로운 구조를 처음부터 학습한 것이 아니라,

25일차에 만든 Spring + MyBatis 구조를 실제 서비스 형태로 확장한 것

이라고 이해할 수 있다.


2. JWT가 실제로 구현된 것인가?

현재는 아니다.

public String createAT(String email) {
    return "Bearer XXXXXXX";
}

현재 JwtProvider는 실제 JWT를 만드는 것이 아니라 임시 문자열을 반환한다.

따라서 현재 단계는

로그인 성공
 ↓
Token Provider 호출
 ↓
Access Token
Refresh Token
 ↓
Client 반환

이라는 인증 구조를 먼저 학습하는 단계라고 이해했다.


3. Password를 그대로 비교해도 되는가?

현재 SQL에서는

WHERE EMAIL = #{email}
AND PASSWORD = #{password}

로 Password를 직접 비교하고 있다.

학습 단계에서는 로그인 흐름을 이해하기 위한 구현이지만 실제 서비스에서는 Password를 평문으로 저장하지 않는다.

일반적으로 BCrypt와 같은 단방향 Hash를 사용하여 Password를 관리한다.


실습 및 적용

직접 해본 것

  • CorsConfig 생성
  • React localhost:3000 Origin 허용
  • UserRequestDTO 작성
  • UserResponseDTO 작성
  • UserMapper Interface 작성
  • MyBatis XML Mapper 작성
  • 회원가입 INSERT SQL 작성
  • 로그인 SELECT SQL 작성
  • UserService 작성
  • 회원가입 Service 구현
  • 로그인 Service 구현
  • Optional을 이용한 로그인 실패 처리
  • JwtProvider 생성
  • Access Token / Refresh Token 임시 발급
  • UserController 회원가입 API 구현
  • UserController 로그인 API 구현
  • ResponseEntity를 이용한 HTTP 응답 작성
  • HttpHeaders에 Token 저장
  • Swagger 회원가입 API 명세 작성
  • Swagger 로그인 API 명세 작성
  • .env 환경변수 사용 구조 확인
  • Dotenv를 이용하여 .env 값 System Property 등록
  • application-dev.yml 작성
  • application-prod.yml 작성
  • Spring Profile 설정
  • MyBatis Mapper XML 위치 설정

결과

회원가입 요청 시

POST /users/signUp

요청이

Controller
 ↓
Service
 ↓
Mapper
 ↓
MyBatis XML
 ↓
SPRING_USER_TBL

순서로 전달되어 사용자 정보가 저장되는 구조를 구현했다.

로그인 시에는

GET /users/signIn
 ↓
UserController
 ↓
UserService
 ↓
UserMapper
 ↓
DB 사용자 확인
 ↓
JwtProvider
 ↓
Access Token / Refresh Token 생성
 ↓
HTTP Header
 ↓
UserResponseDTO 반환

의 흐름을 구현했다.


문제와 해결

막힌 부분

.env에는 어떤 내용을 작성해야 하는가?

application-dev.yml에서는 다음 환경변수를 참조하고 있었다.

driver-class-name: ${DB_DRIVER}
url: ${DB_URL}
username: ${DB_USER}
password: ${DB_PASSWORD}

하지만 실제 값을 어디에 작성해야 하는지 확인이 필요했다.


해결 방법

프로젝트 Root에 .env 파일을 두고 다음과 같이 설정한다.

DB_DRIVER=org.mariadb.jdbc.Driver
DB_URL=jdbc:mariadb://localhost:3306/lgcns
DB_USER=root
DB_PASSWORD=본인의_DB_비밀번호

그리고 InspireMybatisApplication에서 Dotenv를 이용하여 값을 읽는다.

Dotenv env =
    Dotenv.configure()
          .ignoreIfMissing()
          .load();

env.entries().forEach(entry -> {

    System.setProperty(
        entry.getKey(),
        entry.getValue()
    );

});

이후 Spring에서는

${DB_URL}
${DB_USER}
${DB_PASSWORD}

형태로 해당 값을 사용할 수 있다.

또한 .env에는 민감한 정보가 포함될 수 있기 때문에 .gitignore에 추가한다.

.env

26일차 핵심 흐름

[ React ]
localhost:3000

      │
      │ HTTP Request
      ↓

[ CorsConfig ]

      ↓

[ UserController ]

      ↓

[ UserService ]

   ┌──┴─────────────┐
   │                │
   ↓                ↓

[UserMapper]   [JwtProvider]
   │                │
   ↓                ├─ Access Token
[userMapper.xml]    └─ Refresh Token
   │
   ↓
[MariaDB]

      ↓

ResponseEntity

Status
+
Headers
+
Body

      ↓

[ React ]

25일차 → 26일차 학습 발전 과정

25일차

Spring Boot
   ↓
Controller
   ↓
DTO
   ↓
Service
   ↓
Mapper
   ↓
MyBatis XML
   ↓
Database


            ↓ 확장


26일차

React
   ↓
CORS
   ↓
Controller
   ↓
Service
   ├───────────────┐
   ↓               ↓
Mapper         JwtProvider
   ↓               ↓
MyBatis XML     AT / RT
   ↓
Database

+
ResponseEntity
+
HTTP Status
+
HTTP Header
+
Swagger
+
.env
+
Spring Profile

다음에 할 일

  • 실제 JWT Library를 사용하여 Access Token 생성하기
  • 실제 Refresh Token 생성 및 관리 방식 학습하기
  • JWT의 Header / Payload / Signature 구조 이해하기
  • 사용자 Password 암호화 방식 학습하기
  • Spring Security와 JWT 인증 연결하기
  • JWT를 이용한 Authentication / Authorization 흐름 이해하기
  • 로그인 실패 Exception 처리 구조 개선하기
  • React와 Spring 회원가입 / 로그인 API 실제 연동하기
  • dev / prod Profile에 따른 환경설정 차이 확인하기

26일차를 마치며

25일차에는 Spring Boot에서 Controller → Service → Mapper → MyBatis XML → DB로 이어지는 기본적인 백엔드 계층 구조를 이해하는 것이 중심이었다.

26일차에는 이 구조를 그대로 활용하여 실제 회원가입과 로그인 기능을 구현했다.

특히 로그인 성공 이후 JWT Token을 발급한다는 개념이 등장하면서 단순한 DB CRUD를 넘어 사용자 인증(Authentication) 영역으로 학습 범위가 확장되었다.

또한 React와 Spring을 연결하기 위한 CORS, HTTP Header를 통한 Token 전달, .env를 통한 민감정보 분리, Spring Profile을 통한 개발환경과 운영환경 분리까지 학습하면서 프로젝트가 단순한 Test Case에서 실제 웹 서비스에 가까운 구조로 발전했다.

26일차의 핵심은 새로운 Spring 구조를 다시 배우는 것이 아니라,

25일차에 배운 Spring + MyBatis 기본 구조 위에 회원가입, 로그인, JWT, CORS, 환경설정을 추가하여 실제 서비스 구조로 확장하는 과정

이라고 정리할 수 있다.

profile
개발 공부 기록

0개의 댓글