์ด์ง€๋ฏผ์˜ ๊ฐœ๋ฐœ ๋…ธํŠธ

๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์„ ๊ณต๋ถ€ํ•˜๋ฉฐ Java์™€ Spring์„ ์ค‘์‹ฌ์œผ๋กœ ํ•™์Šต ๊ธฐ๋ก์„ ์ •๋ฆฌํ•˜๋Š” ๊ณต๊ฐ„์ž…๋‹ˆ๋‹ค.

๐Ÿ”ฅ ๋‚ด์ผ๋ฐฐ์›€์บ ํ”„/๐Ÿ“Œ ๊ณผ์ œ

[๋‚ด์ผ๋ฐฐ์›€์บ ํ”„ Spring 3๊ธฐ] ๊ณ ๊ฐ ๋ฐ ์ƒํ’ˆ ๊ด€๋ฆฌ ์‹œ์Šคํ…œ ํ”„๋กœ์ ํŠธ (์ด์ปค๋จธ์Šค ๋ฐฑ์˜คํ”ผ์Šค) - API, ์„ธ์…˜

jiiimni 2026. 3. 10. 12:32

[Spring] ๋ฐฑ์˜คํ”ผ์Šค ๊ด€๋ฆฌ์ž ์ธ์ฆ(์„ธ์…˜) ๊ตฌํ˜„ ํšŒ๊ณ  & ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… (ํšŒ์›๊ฐ€์ž…/๋กœ๊ทธ์ธ/๋กœ๊ทธ์•„์›ƒ/์„ธ์…˜ ์„ค์ •)

ํŒ€ ํ”„๋กœ์ ํŠธ์—์„œ “๋ฐฑ์˜คํ”ผ์Šค ๊ด€๋ฆฌ์ž” ํŒŒํŠธ๋ฅผ ๋งก๊ฒŒ ๋˜๋ฉด์„œ, ํ•„์ˆ˜ ๊ธฐ๋Šฅ ์ค‘ ์„ธ์…˜ ๊ธฐ๋ฐ˜ ์ธ์ฆ(์ฟ ํ‚ค/์„ธ์…˜)์„ ๋จผ์ € ๊ตฌํ˜„ํ–ˆ๋‹ค.

๊ตฌํ˜„ํ•œ ๊ธฐ๋Šฅ ๋ชฉ๋ก (ํ•„์ˆ˜ 4๊ฐœ)

  • ๊ด€๋ฆฌ์ž ํšŒ์›๊ฐ€์ž… API (๊ธฐ๋ณธ ์ƒํƒœ: ์Šน์ธ๋Œ€๊ธฐ PENDING, ๋น„๋ฐ€๋ฒˆํ˜ธ ์•”ํ˜ธํ™” ์ €์žฅ)
  • ๊ด€๋ฆฌ์ž ๋กœ๊ทธ์ธ API (์„ธ์…˜ ์ƒ์„ฑ ๋ฐ ์ €์žฅ, ACTIVE๋งŒ ๋กœ๊ทธ์ธ ๊ฐ€๋Šฅ)
  • ๊ด€๋ฆฌ์ž ๋กœ๊ทธ์•„์›ƒ API (์„ธ์…˜ invalidate)
  • ์„ธ์…˜ ๋งŒ๋ฃŒ ์‹œ๊ฐ„/์ฟ ํ‚ค ์˜ต์…˜(HttpOnly) ์„ค์ •

1. ํŒ€ ์ปจ๋ฒค์…˜์— ๋งž์ถ˜ ํŒจํ‚ค์ง€ ๊ตฌ์กฐ

ํŒ€ ํ”„๋กœ์ ํŠธ๋ผ์„œ ๋‚ด ๋งˆ์Œ๋Œ€๋กœ ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค๋ฉด ๋‚˜์ค‘์— ์ถฉ๋Œ์ด ๋‚˜๊ฑฐ๋‚˜ ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์–ด๋ ค์›Œ์งˆ ์ˆ˜ ์žˆ๋‹ค.
ํ”„๋กœ์ ํŠธ ๋‚ด๋ถ€๋ฅผ ๋ณด๋‹ˆ ๋„๋ฉ”์ธ๋ณ„๋กœ ๋ฌถ๋Š” ๋ฐฉ์‹์ด ์‚ฌ์šฉ๋˜๊ณ  ์žˆ์—ˆ๊ณ , ๊ณตํ†ต ์‘๋‹ต/์˜ˆ์™ธ ํฌ๋งท๋„ ์ด๋ฏธ ์กด์žฌํ–ˆ๋‹ค.

๊ทธ๋ž˜์„œ ๊ด€๋ฆฌ์ž ์ธ์ฆ์€ ์•„๋ž˜์ฒ˜๋Ÿผ admin ๋„๋ฉ”์ธ ํŒจํ‚ค์ง€๋ฅผ ๋งŒ๋“ค๊ณ  ๊ทธ ์•ˆ์—์„œ 3-layer๋ฅผ ์œ ์ง€ํ–ˆ๋‹ค.

com.example.commercepilot
 โ”ฃ admin
 โ”ƒ โ”ฃ controller
 โ”ƒ โ”ฃ dto
 โ”ƒ โ”ƒ โ”ฃ request
 โ”ƒ โ”ƒ โ”ฃ response
 โ”ƒ โ”ƒ โ”— session
 โ”ƒ โ”ฃ entity
 โ”ƒ โ”ฃ repository
 โ”ƒ โ”ฃ service
 โ”ฃ config
 โ”ฃ exception

ํŒ€ ๊ณตํ†ต ํฌ๋งท ์žฌ์‚ฌ์šฉ

  • ApiResponse (์„ฑ๊ณต/์‹คํŒจ ์‘๋‹ต ๊ณตํ†ต)
  • ErrorCode, ErrorResponse
  • GlobalExceptionHandler (CustomException + Validation ์ฒ˜๋ฆฌ)

ํŒ€ ์ปจ๋ฒค์…˜์„ ์ตœ๋Œ€ํ•œ ๋”ฐ๋ผ๊ฐ€๋ฉด์„œ, ๋‚ด๊ฐ€ ๊ตฌํ˜„ํ•˜๋Š” ๊ธฐ๋Šฅ๋งŒ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ถ”๊ฐ€๋˜๋„๋ก ํ–ˆ๋‹ค.


2. ๊ด€๋ฆฌ์ž ํšŒ์›๊ฐ€์ž… API ๊ตฌํ˜„

2-1. ์š”๊ตฌ์‚ฌํ•ญ ํ•ต์‹ฌ ์ •๋ฆฌ

ํšŒ์›๊ฐ€์ž…์—์„œ ๋†“์น˜๋ฉด ์•ˆ ๋˜๋Š” ํฌ์ธํŠธ๋Š” ์•„๋ž˜์˜€๋‹ค.

  • ์ž…๋ ฅ๊ฐ’ ์œ ํšจ์„ฑ ๊ฒ€์ฆ (์ด๋ฉ”์ผ ํ˜•์‹, ๋น„๋ฐ€๋ฒˆํ˜ธ ์ตœ์†Œ 8์ž, ์ „ํ™”๋ฒˆํ˜ธ 010-XXXX-XXXX, role ํ•„์ˆ˜ ๋“ฑ)
  • ์ด๋ฉ”์ผ ์ค‘๋ณต ์ฒดํฌ
  • ๋น„๋ฐ€๋ฒˆํ˜ธ ์•”ํ˜ธํ™” ์ €์žฅ (BCrypt ๊ธฐ๋ฐ˜ PasswordEncoder)
  • ๊ฐ€์ž… ์‹œ ๊ธฐ๋ณธ ์ƒํƒœ: ์Šน์ธ๋Œ€๊ธฐ(PENDING)

2-2. DTO(Validation) ์„ค๊ณ„ ์ด์œ 

ํšŒ์›๊ฐ€์ž… ์š”์ฒญ DTO๋Š” record๋กœ ๋งŒ๋“ค๊ณ  Bean Validation์„ ๋ถ™์˜€๋‹ค.
Controller์—์„œ @Valid๋ฅผ ๋ถ™์ด๋ฉด Validation ์‹คํŒจ ์‹œ MethodArgumentNotValidException์ด ๋ฐœ์ƒํ•˜๊ณ , ํŒ€ ๊ณตํ†ต GlobalExceptionHandler์—์„œ ApiResponse.fail(INVALID_INPUT_VALUE, fieldErrors) ํ˜•ํƒœ๋กœ ๋‚ด๋ ค์ค„ ์ˆ˜ ์žˆ๋‹ค.

public record AdminSignupRequest(
    @NotBlank String name,
    @Email @NotBlank String email,
    @Size(min = 8) @NotBlank String password,
    @Pattern(regexp = "^010-\\d{4}-\\d{4}$") @NotBlank String callNumber,
    @NotNull AdminRole role
) {}

์‘๋‹ต DTO๋Š” ๋ฏผ๊ฐ์ •๋ณด(๋น„๋ฐ€๋ฒˆํ˜ธ)๋Š” ์ ˆ๋Œ€ ํฌํ•จํ•˜์ง€ ์•Š๋„๋ก ์ตœ์†Œ ํ•„๋“œ๋งŒ ์ •์˜ํ–ˆ๋‹ค.

public record AdminSignupResponse(
    Long id,
    String name,
    String email,
    String callNumber,
    AdminRole role,
    AdminStatus status,
    LocalDateTime createdAt
) { ... }

๐Ÿ”Ž ์™œ password๋ฅผ ์‘๋‹ต์— ์•ˆ ๋„ฃ๋Š”๊ฐ€?

๋ฆฌ๋ทฐ์—์„œ๋„ ์‹ค์ œ๋กœ ๋‚˜์˜จ ํฌ์ธํŠธ์ธ๋ฐ, ํ•ด์‹œ๋œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ผ๋„ ์‘๋‹ต์œผ๋กœ ๋‚ด๋ ค์ฃผ๋Š” ๊ฑด ๋ณด์•ˆ์ƒ ์ ์ ˆํ•˜์ง€ ์•Š๋‹ค.
๊ทธ๋ž˜์„œ ์‘๋‹ต DTO์— password๋Š” ์•„์˜ˆ ํฌํ•จ์‹œํ‚ค์ง€ ์•Š์•˜๋‹ค.


2-3. Service ๋กœ์ง ์„ค๊ณ„ ์ด์œ  (CommandService)

ํšŒ์›๊ฐ€์ž…์€ “๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•˜๋Š”(์“ฐ๊ธฐ)” ๋กœ์ง์ด๋‹ค.
๊ทธ๋ž˜์„œ ํŒ€์—์„œ ์ถ”์ฒœํ•˜๋˜ CQRS ๋ง›๋ณด๊ธฐ ๊ตฌ์กฐ์ฒ˜๋Ÿผ, ์“ฐ๊ธฐ ๋กœ์ง์€ CommandService์— ๋‘๊ณ  ํ™•์žฅ ๊ฐ€๋Šฅํ•˜๋„๋ก ๊ตฌ์„ฑํ–ˆ๋‹ค.

@Transactional
public AdminSignupResponse signup(AdminSignupRequest request) {
    if (adminRepository.existsByEmail(request.email())) {
        throw new CustomException(ErrorCode.ADMIN_EMAIL_DUPLICATED);
    }

    String encoded = passwordEncoder.encode(request.password());

    Admin admin = Admin.pending(
        request.name(),
        request.email(),
        encoded,
        request.callNumber(),
        request.role()
    );

    Admin saved = adminRepository.save(admin);
    return AdminSignupResponse.from(saved);
}

์—ฌ๊ธฐ์„œ ๋‚ด๊ฐ€ ์‹ ๊ฒฝ์“ด ๋ถ€๋ถ„

  • ์ค‘๋ณต ์ด๋ฉ”์ผ์€ existsByEmail๋กœ ๋น ๋ฅด๊ฒŒ ์ฒดํฌ
  • ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” PasswordEncoder.encode()๋กœ ์ €์žฅ ์ง์ „์— ์•”ํ˜ธํ™”
  • ์ƒํƒœ๋Š” ๋ฌด์กฐ๊ฑด PENDING์œผ๋กœ ๊ณ ์ • (์Šน์ธ ์ „์—๋Š” ๋กœ๊ทธ์ธ ๋ถˆ๊ฐ€)

3. ๋กœ๊ทธ์ธ API ๊ตฌํ˜„ (์„ธ์…˜ ๊ธฐ๋ฐ˜ ์ธ์ฆ)

3-1. ์š”๊ตฌ์‚ฌํ•ญ ํ•ต์‹ฌ

๋กœ๊ทธ์ธ์€ ๋‹จ์ˆœํžˆ ์ด๋ฉ”์ผ/๋น„๋ฐ€๋ฒˆํ˜ธ๋งŒ ๋งž์œผ๋ฉด ๋์ด ์•„๋‹ˆ๋ผ, ๊ณ„์ • ์ƒํƒœ์— ๋”ฐ๋ผ ๋กœ๊ทธ์ธ ๊ฐ€๋Šฅ ์—ฌ๋ถ€๊ฐ€ ๊ฐˆ๋ฆฐ๋‹ค.

  • ACTIVE๋งŒ ๋กœ๊ทธ์ธ ๊ฐ€๋Šฅ
  • PENDING/REJECTED/SUSPENDED/INACTIVE๋Š” ๋กœ๊ทธ์ธ ๋ถˆ๊ฐ€ + ๋ช…ํ™•ํ•œ ๋ฉ”์‹œ์ง€

๋˜ํ•œ ๋กœ๊ทธ์ธ ์„ฑ๊ณต ์‹œ์—๋Š” ์„œ๋ฒ„ ์„ธ์…˜์— ์•„๋ž˜ ์ •๋ณด๋ฅผ ์ €์žฅํ•ด์•ผ ํ•œ๋‹ค.

  • adminId
  • email
  • role

3-2. ์„ธ์…˜ ์ €์žฅ DTO + ์„ธ์…˜ ํ‚ค ์ƒ์ˆ˜

์„ธ์…˜์— ์ €์žฅํ•˜๋Š” ๋ฐ์ดํ„ฐ๋Š” ์ตœ์†Œํ•œ์œผ๋กœ, ๊ทธ๋ฆฌ๊ณ  ๋ช…ํ™•ํ•˜๊ฒŒ ํ•˜๋ ค๊ณ  ๋ณ„๋„ record๋กœ ๋บ๋‹ค.

public record LoginAdmin(Long adminId, String email, AdminRole role) {}

์„ธ์…˜ key๋Š” ์˜คํƒ€ ๋ฐฉ์ง€ ์œ„ํ•ด ์ƒ์ˆ˜๋กœ ๊ด€๋ฆฌํ–ˆ๋‹ค.

public final class SessionConst {
    public static final String LOGIN_ADMIN = "LOGIN_ADMIN";
}

3-3. ๋กœ๊ทธ์ธ Service ๋กœ์ง (์ƒํƒœ๋ณ„ ๋ถ„๊ธฐ)

@Transactional(readOnly = true)
public void login(AdminLoginRequest request, HttpSession session) {
    Admin admin = adminRepository.findByEmail(request.email())
        .orElseThrow(() -> new CustomException(ErrorCode.LOGIN_FAILED));

    if (!passwordEncoder.matches(request.password(), admin.getPassword())) {
        throw new CustomException(ErrorCode.LOGIN_FAILED);
    }

    validateLoginStatus(admin.getStatus());

    LoginAdmin loginAdmin = new LoginAdmin(admin.getId(), admin.getEmail(), admin.getRole());
    session.setAttribute(SessionConst.LOGIN_ADMIN, loginAdmin);
}

๐Ÿ”Ž ์™œ ์ด๋ฉ”์ผ ์—†์Œ / ๋น„๋ฒˆ ํ‹€๋ฆผ์€ ๊ฐ™์€ ์—๋Ÿฌ(LOGIN_FAILED)๋กœ ์ฒ˜๋ฆฌํ–ˆ๋‚˜?

๋ณด์•ˆ์ƒ “์ด๋ฉ”์ผ์ด ์กด์žฌํ•˜๋Š”์ง€”๋ฅผ ๋…ธ์ถœํ•˜๋ฉด ์•ˆ ๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋ณดํ†ต ๋กœ๊ทธ์ธ ์‹คํŒจ๋Š” ๋™์ผํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•œ๋‹ค.
๊ทธ๋ž˜์„œ ๊ณ„์ • ์กด์žฌ ์—ฌ๋ถ€๋Š” ์ˆจ๊ธฐ๊ณ , ์ƒํƒœ(์Šน์ธ๋Œ€๊ธฐ/์ •์ง€ ๋“ฑ)๋Š” ์š”๊ตฌ์‚ฌํ•ญ์— ๋งž๊ฒŒ ๋ช…ํ™•ํžˆ ๋ถ„๊ธฐํ–ˆ๋‹ค.


4. ๋กœ๊ทธ์•„์›ƒ API ๊ตฌํ˜„

๋กœ๊ทธ์•„์›ƒ์€ ๊ฐ„๋‹จํžˆ ์„ธ์…˜ ๋ฌดํšจํ™”๋งŒ ํ•˜๋ฉด ๋œ๋‹ค.

public void logout(HttpSession session) {
    session.invalidate();
}

์ปจํŠธ๋กค๋Ÿฌ์—์„œ๋Š” ์•„๋ž˜์ฒ˜๋Ÿผ ์‘๋‹ต data ์—†์ด ์„ฑ๊ณต๋งŒ ๋‚ด๋ ค์ฃผ๋„๋ก ํ–ˆ๋‹ค.

@PostMapping("/logout")
public ApiResponse<Void> logout(HttpSession session) {
    adminAuthService.logout(session);
    return ApiResponse.success(HttpStatus.OK);
}

5. ์„ธ์…˜ ๋งŒ๋ฃŒ์‹œ๊ฐ„/์ฟ ํ‚ค(HttpOnly) ์„ค์ •

์„œ๋ฒ„์—์„œ ์„ธ์…˜์„ ์˜ค๋ž˜ ์œ ์ง€ํ• ์ง€ ๊ฒฐ์ •ํ•ด์•ผ ํ•œ๋‹ค.
ํ•„์ˆ˜ ์š”๊ตฌ์‚ฌํ•ญ ์˜ˆ์‹œ๊ฐ€ 24์‹œ๊ฐ„์ด์—ˆ๊ณ , ์„ธ์…˜ ์ฟ ํ‚ค๋Š” XSS ์œ„ํ—˜์„ ์ค„์ด๊ธฐ ์œ„ํ•ด HttpOnly ์˜ต์…˜์„ ์‚ฌ์šฉํ–ˆ๋‹ค.

application.properties

server.servlet.session.timeout=24h
server.servlet.session.cookie.http-only=true

6. ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…

6-1. DTO ํŒจํ‚ค์ง€ ๊ตฌ์กฐ ๋•Œ๋ฌธ์— “import ์˜ค๋ฅ˜”๊ฐ€ ๋‚ฌ๋‹ค

์ฒ˜์Œ์—๋Š” DTO๋ฅผ admin/dto ์•„๋ž˜์— ๋ฐ”๋กœ ๋‘๊ณ  import๋ฅผ ์ž‘์„ฑํ–ˆ๋Š”๋ฐ, ํŒ€ ๊ตฌ์กฐ๋ฅผ ๋”ฐ๋ผ๊ฐ€๋‹ค ๋ณด๋‹ˆ dto/request, dto/response๋กœ ํด๋”๋ฅผ ๋‚˜๋ˆด๋‹ค.

๊ทธ ์ƒํƒœ์—์„œ ์„œ๋น„์Šค ์ฝ”๋“œ๊ฐ€ ์•„๋ž˜์ฒ˜๋Ÿผ ์ž‘์„ฑ๋˜์–ด ์žˆ์œผ๋ฉด:

import com.example.commercepilot.admin.dto.AdminSignupRequest;

์‹ค์ œ DTO ๊ฒฝ๋กœ๋Š”:

com.example.commercepilot.admin.dto.request.AdminSignupRequest

์ด๋ผ์„œ IntelliJ์—์„œ Cannot resolve symbol ์—๋Ÿฌ๊ฐ€ ๊ณ„์† ๋‚ฌ๋‹ค.

ํ•ด๊ฒฐ:

  • DTO ํŒŒ์ผ์˜ package ์„ ์–ธ์„ ํด๋” ๊ตฌ์กฐ์— ๋งž์ถ”๊ณ ,
  • Service/Controller import๋„ ์ •ํ™•ํžˆ ๋งž์ถฐ์คฌ๋‹ค.

6-2. “์•”ํ˜ธํ™”๋œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์‘๋‹ต์— ๋‹ด๋Š” ๊ฒŒ ์ ์ ˆํ•œ๊ฐ€์š”?” ๋ฆฌ๋ทฐ ๋Œ€์‘

์„œ๋น„์Šค ๋กœ์ง์— encoded ๋ณ€์ˆ˜๊ฐ€ ๋ณด์ด๋‹ค ๋ณด๋‹ˆ, ๋ฆฌ๋ทฐ์–ด๊ฐ€ “์‘๋‹ต์— password๊ฐ€ ํฌํ•จ๋˜๋Š”์ง€”๋ฅผ ๊ฑฑ์ •ํ•ด์ฃผ์…จ๋‹ค.

์‹ค์ œ๋กœ๋Š” AdminSignupResponse์— password ํ•„๋“œ๊ฐ€ ์—†๊ณ , from()์—์„œ๋„ ๋งคํ•‘ํ•˜์ง€ ์•Š์•„ ์‘๋‹ต์œผ๋กœ ๋‚˜๊ฐ€์ง€ ์•Š๋Š”๋‹ค.

โœ… ๋Œ€์‘:

  • AdminSignupResponse์— password๊ฐ€ ์—†์Œ์„ ์ฝ”๋“œ๋กœ ์„ค๋ช…
  • “ํ•ด์‹œ๋ผ๋„ ์‘๋‹ต์— ํฌํ•จํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์›์น™”์ด๋ผ๋Š” ์ ์„ ํ™•์ธ

(์ถ”๊ฐ€๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ํ•˜๋ ค๋ฉด ์—”ํ‹ฐํ‹ฐ password์— @JsonIgnore๋ฅผ ๋ถ™์—ฌ ์‹ค์ˆ˜๋กœ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•ด๋„ ๋…ธ์ถœ๋˜์ง€ ์•Š๊ฒŒ ๋ง‰์„ ์ˆ˜๋„ ์žˆ๋‹ค.)


7. ํšŒ๊ณ 

์ด๋ฒˆ ์ธ์ฆ ํŒŒํŠธ๋ฅผ ํ•˜๋ฉด์„œ ๋А๋‚€ ๊ฑด, ๊ธฐ๋Šฅ ์ž์ฒด๋ณด๋‹ค ํŒ€ ์ปจ๋ฒค์…˜์— ๋งž์ถฐ ๊ตฌ์กฐ๋ฅผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ผ์›Œ ๋„ฃ๋Š” ๊ฒƒ์ด ๋” ์ค‘์š”ํ–ˆ๋‹ค๋Š” ์ ์ด๋‹ค.

  • “๋‚ด๊ฐ€ ์ต์ˆ™ํ•œ ๊ตฌ์กฐ”๋Œ€๋กœ ๋งŒ๋“ค๋ฉด ๋นจ๋ฆฌ ๋งŒ๋“ค ์ˆ˜ ์žˆ์ง€๋งŒ,
  • ํŒ€ ํ”„๋กœ์ ํŠธ๋Š” ๊ฒฐ๊ตญ ๋‹ค ๊ฐ™์ด ์œ ์ง€๋ณด์ˆ˜ํ•  ์ˆ˜ ์žˆ๋Š” ๊ตฌ์กฐ๊ฐ€ ๋” ๊ฐ€์น˜๊ฐ€ ํฌ๋‹ค.

๋˜ํ•œ ๋กœ๊ทธ์ธ/ํšŒ์›๊ฐ€์ž…์€ ์‹ค๋ฌด์—์„œ๋„ ๋ฏผ๊ฐํ•œ ์˜์—ญ์ด๋ผ

  • ์‘๋‹ต์— ๋ฏผ๊ฐ์ •๋ณด ํฌํ•จ ์—ฌ๋ถ€
  • ์‹คํŒจ ์‘๋‹ต ๋ฉ”์‹œ์ง€ ์ •์ฑ…
  • ์„ธ์…˜ ๋ณด์•ˆ ์˜ต์…˜(HttpOnly)
    ๊ฐ™์€ ์„ธ๋ถ€ ํฌ์ธํŠธ๊ฐ€ ์ค‘์š”ํ•˜๋‹ค๋Š” ๊ฑธ ์ฒด๊ฐํ–ˆ๋‹ค.

8. ๋‹ค์Œ ํ•  ์ผ

์ธ์ฆ(์„ธ์…˜) 4๊ฐœ๋ฅผ ๋๋‚ธ ๋’ค์—๋Š” ๊ด€๋ฆฌ์ž ๊ธฐ๋Šฅ์ด ๋ณธ๊ฒฉ์ ์œผ๋กœ ๋‚จ์•„์žˆ๋‹ค.

  • ๊ด€๋ฆฌ์ž ์Šน์ธ/๊ฑฐ๋ถ€(์Šˆํผ๊ด€๋ฆฌ์ž ์„ธ์…˜ ๊ธฐ๋ฐ˜)
  • ๋‚ด ํ”„๋กœํ•„ ์กฐํšŒ/์ˆ˜์ •
  • ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ
  • ๊ด€๋ฆฌ์ž ๋ชฉ๋ก/๊ฒ€์ƒ‰/์ •๋ ฌ/ํ•„ํ„ฐ/ํŽ˜์ด์ง•
  • ๊ด€๋ฆฌ์ž ์ƒ์„ธ/์ˆ˜์ •/์‚ญ์ œ/์—ญํ•  ๋ณ€๊ฒฝ/์ƒํƒœ ๋ณ€๊ฒฝ

๋‹ค์Œ ๊ธ€์—์„œ๋Š” “์Šˆํผ๊ด€๋ฆฌ์ž ์Šน์ธ ํ”„๋กœ์„ธ์Šค(์Šน์ธ๋Œ€๊ธฐ -> ํ™œ์„ฑ)”์„ ๊ตฌํ˜„ํ•˜๋ฉด์„œ ์ƒ๊ธด ์ด์Šˆ๋“ค์„ ์ •๋ฆฌํ•  ์˜ˆ์ •์ด๋‹ค.