Post

9일차 로그인 화면 기능 구현, 전체 컴포넌트 권한 로직 적용

9일차 로그인 화면 기능 구현, 전체 컴포넌트 권한 로직 적용

🎯 오늘의 목표

현재 토큰을 발급하고, 인증하는 로직은 개발이 되어있다. 실제로 화면에서 로그인을 했을때 JWT 토큰을 발급하고 저장할 수 있도록 구현해보자. 그리고, API 요청시에도 토큰 유무를 확인한 후 호출될 수 있도록 구현하자.

  • 사용자 로그인 기능 구현 (이메일 + 비밀번호)
  • 로그인 성공 시 JWT 토큰 발급 및 저장
  • 모든 API 요청에 Authorization 헤더 자동 포함

🔐 백엔드(Spring Boot)

1. 왜 수정했는가?

기존 로그인 API는 단순히 /login 엔드포인트로 요청을 받아 인증만 하고 끝이었음.
하지만 프론트에서 받은 토큰이 백엔드에서 보호된 API에 접근할 수 없었다. JWT 토큰을 발급은 했지만, 그걸 Authorization 헤더로 넘기지 않으면 인증이 안됨.
게다가 Spring Security에서는 기본적으로 /login은 폼 로그인용이고, 현재 개발해서 사용하는 REST 방식에서는 예외로 처리해야 함.

2. 수정 핵심 부분

  • /auth/login으로 URL 경로 명시 → Spring Security에서 이 경로는 허용
  • 로그인 시 AuthenticationManager로 사용자 인증 → UserDetailsService 통해 DB에서 사용자 조회
  • 인증에 성공하면 JWT 토큰을 직접 발급해서 반환

3. 최종 로그인 API 소스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@RestController
@RequiredArgsConstructor
@RequestMapping("/auth")
public class AuthController {

    private final AuthenticationManager authenticationManager;
    private final JwtTokenProvider jwtTokenProvider;

    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginRequest request) {
        Authentication authentication = authenticationManager.authenticate(
            new UsernamePasswordAuthenticationToken(request.getEmail(), request.getPassword())
        );

        User user = (User) authentication.getPrincipal();

        // username은 email, role은 첫 번째 권한
        String token = jwtTokenProvider.createToken(user.getUsername(), user.getAuthorities().iterator().next().getAuthority());

        return ResponseEntity.ok().body(token);
    }
}

🖥️ 프론트엔드(Vue 3)

1. 로그인 요청

1
2
3
4
5
6
7
const response = await axios.post('/login', {
  email: loginForm.email,
  password: loginForm.password
});
const token = response.data;
localStorage.setItem('token', token);
router.push('/dashboard');

2. 토큰 자동 삽입 (main.js)

1
2
3
4
5
axios.interceptors.request.use(config => {
  const token = localStorage.getItem('token');
  if (token) config.headers.Authorization = `Bearer ${token}`;
  return config;
});

3. 전체 컴포넌트 API 호출 fetch → axios 전환

📌 fetch → axios 전환 이유

비교 항목fetchaxios
응답 처리.then(...).json() 필요res.data 바로 사용 가능
에러 처리.ok 체크 필요 + try-catchtry-catch로 충분
토큰 헤더 처리매 요청마다 직접 넣어야 함인터셉터로 전역 자동 처리 가능

🛠️ 트러블슈팅 기록

1. fetch → axios 변경 시 .json() 에러

1
2
3
4
5
6
// 잘못된 코드
const response = await axios.get('/api/data');
const data = await response.json(); // 오류 발생

// 올바른 코드
const { data } = await axios.get('/api/data');

2. API 호출은 되는데 403 에러

  • 증상: 로그인은 성공하지만 이후 대시보드 API 요청 시 403 Forbidden 발생
  • 원인: Authorization 헤더가 누락됨
  • 해결: axios.interceptors.requestmain.js에 추가하여 모든 요청에 토큰 자동 포함

🔚 마무리

fetch에서 axios로 전환하면서 전역 인증 처리 및 유지보수 편의성이 확실히 상승된 것 같다. 왜 처음부터 이런 생각을 못하고 무작정 fetch로만 구현했을까? 또 한번 배울 수 있는 기회가 되었다.

그리고, 모든 API 보안 접근제어는 JWT 토큰 존재 여부로 통제되는 구조로 구현을 하게 되어 보안성이 크게 향상되었다. 직접 설계를 하고 개발을 해보기 전에는 항상 궁금했던 부분인데 이렇게 직접 구현해보니 프로세스가 완벽히 이해가 되어 뿌듯하다.

Frontend Repository : GitHub

Backend Repository : GitHub

This post is licensed under CC BY 4.0 by the author.