Project

to-do 애플리케이션 개발

성하 盛夏 2023. 7. 28. 17:55

1. 기본 설정

spring initializr 을 통해 프로젝트 생성 후
import, model 패키지 생성-todoEntity.java 클래스 생성


2. 모델-엔티티 클래스 구현

TODOENTITY 클래스에 다음과 같이 필드 작성


3.DTO 구현

*DTO(Data Transfer Object)

더보기

 : 데이터를 전달하는 데 사용하는 오브젝트.

 

사용 목적

-비즈니스 로직의 캡슐화 : 외부 사용자에게서 서비스 내부의 로직, DB 구조 등을 숨기기 위해(모델은 DB 테이블 구조와 매우 유사하며, 모델이 갖고 있는 필드는 테이블의 스키마와 비슷할 확률이 높아 외부인이 이를 알 수 있는 가능성이 있음)

-클라이언트가 필요한 정보를 모델이 전부 포함하지 않는 경우가 많음 : 대표적인 예시로 에러 메시지가 있으며, 서비스 실행 도중 사용자 에러가 날 경우 이 에러 메시지 필드를 DTO 에 선언 후 포함하면 됨.

이렇게 dto 패키지 생성 -> tododto 클래스 생성
todo아이템의 생성, 수정, 삭제 역할)todoDTO 클래스 작성
HTTP 응답용의 새로운 클래스 ResponseDTO 생성 - 다음과 같이 작성

더보기

*자바 Generic : 

 

사용 목적 : 다른 모델의 DTO가 이를 이용해 리턴할 수 있도록 Generic 이용


4. 컨트롤러 레이어 : REST API 컨트롤러

controller 관련 클래스를 모을 controller 패키지 생성 - testController.java 클래스 생성 후 다음과 같이 작성
실행하면 짜잔~ 이렇게 나온다! test 리소스에 get 요청이 들어오면 해당 화면이 실행됨!

+)매개변수 넘겨받기

1.@PathVariable 사용하기

같은 클래스 내에 새로운 메서드를 추가해보자
그럼 이렇게 URI의 경로로 넘어오는 값을 변수로 받을 수 있음!

2.@RequestParam 사용하기

이번에는 @RequestParam 을 사용하여...
짠~ uri 뒤에 ?파라미터명=값 으로 넘어온걸 아래서 출력할 수 있음

 

3.@RequestBody 사용하기

실습을 위해 새로운 DTO 생성

 

새로운 메서드를 생성해주고 포스트맨으로 실행해보면
짠 body-json으로 넘어간 게 아래에 출력된 걸 볼 수 있음!

4.@ResponseBody 사용하기

-오브젝트 리턴 시 사용!

새로운 메서드 생성
포스트맨을 통해 받아보면 요렇게 나옴! 밑에 response body를 통해 넘어온 값이 출력됨!

5.@ResponseEntity 사용하기

ResponseEntity를 반환하기 위한 새로운 메서드 생성
포스트맨을 통해 받아보면 요렇게 나옴! 이렇게 400 Bad Request가 반환된 것이 보임!
이번에는 ok로! 답을 고정해서!
아래에 ok! 성공인 200 코드가 출력됨을 확인할 수 있음

-> 리턴된 body 외에 큰 차이점이 보이지 않지만, 헤더와 HTTP Status를 조작할 수 있는 쪽은 Entity쪽임!! 


5. 서비스 레이어 : 비즈니스 로직

: 컨트롤러-퍼시스턴스 사이에서 비즈니스 로직을 수행하는 역할. 컨트롤러와 퍼시스턴스에서 분리되어 있음.

새로운 서비스 패키지를 만들고, 여기에 새로운 클래스를 만듦!
기존의 컨트롤러를 수정해주고, 포스트맨을 통해 실현시켜줌
포스트맨을 통해 test Service가 리턴된 것을 확인할 수 있음!


6. 퍼시스턴스 레이어 : JPA

*JPA

더보기

: 스펙(Specification) - 구현을 위해 작성해야 하는 기능을 알려주는 지침이 되는 문서. 자바에서 DB 접근, 저장, 관리에 필요한 스펙으로, 이 스펙을 구현하는 구현자를 JPA Provider라고 부르며 그 중 대표적인 것이 Hibernate임

: 반복해서 DB 쿼리를 보내 ResultSet을 파싱해야하는 과정을 줄여줌! 

*스프링 데이터 JPA

: JPA+@의 개념. JPA를 더 사용하기 쉽게 도와주는 스프링의 프로젝트인데, 기술적으로는 이를 추상화(=사용하기 쉬운 인터페이스를 제공함)한다고 함.

 

*H2 DB

더보기

: In-Memory 데이터베이스. 로컬 환경에서 메모리상에 DB를 구축해줌.  사용 시 따로 DB 서버를 구축하는 데에 시간을 할애할 필요가 없어, 초기 개발 시 많이 사용. In-Memory 로 실행할 경우 애플리케이션 실행 시 테이블이 생성되고, 애플리케이션 종료 시에는 테이블이 함께 소멸됨

보통 DB 테이블마다 그에 상응하는 엔티티 클래스가 존재하는데, 엔티티 클래스는 클래스 그 자체가 테이블을 정의해야 함! 즉, ORM이 엔티티를 보고 어떤 테이블의 어떤 필드에 매핑해야 하는지 알 수 있어야 하며 어떤 필드가 기본키 / 외래키인지 구분이 가능해야 함. 이런 DB 테이블 스키마에 관한 정보는 javax.persistence가 제공하는 JPA관련 어노테이션을 이용해 정의함.

 

*자바 클래스를 엔티티로 정의할 때 주의해야할 점

-클래스에는 매개변수가 없는 생성자(=NoArgsConstructor)가 필요함

-Getter/Setter가 필요함

-기본키 지정 필요

 

먼저 model 패키지의 TodoEntity 클래스에서 기본키가 되어줄 id 위에 @Id 어노테이션을 추가해주고
@GenerateValue, @GenericGenerator 어노테이션까지 추가해주어용~

문자열 형태의 UUID의 사용을 위해 커스텀 Generator를 만들기 위해 @GenericGenerator를 이용

→매개변수 strategy로 uuid를 넘기고, 이렇게 uuid를 사용하는 system-uuid 라는 이름의 GenericGenerator를 만들었고 이를 @GeneratedValue가 참조해서 사용함

 

persistence 패키지 생성-TodoRepository 클래스 생성
그리고.... extends로 JpaRepository*를 상속받도록 추가 작성!

*JpaRepository<T, ID> 

-T ; 테이블에 매핑될 엔티티 클래스

-ID ; 엔티티의 기본 키 타입

 

→ 테이핑에 매핑될 엔티티 클래스인 TodoEntity, 기본키 타입인 String을 넣어줌

 

테스팅을 위해 서비스에 TodoEntity 오브젝트를 추가/수정하고...&nbsp; 서비스 실행-포스트맨으로 돌려보면
위와 같이 작성한 응답이 리턴되는 것을 확인할 수 있음


*AOP(Aspect Oriented Programming)

 

 


기본적인 쿼리인 경우 JpaRepository를 상속하고,  JpaRepository가 기본적인 DB 오퍼레이션 인터페이스를 제공한다. 이 인터페이스를 스프링 데이터 JPA가 실행 시 구현해주기 때문에 SQL쿼리를 따로 작성할 필요가 없음

 

이번에는 인터페이스에 findByUserId 메서드를 작성해주고

 

기본적인 쿼리가 아닌 경우) 메서드를 작성하면, 메서드 이름은 쿼리 / 매개변수는 쿼리의 where문에 들어갈 값을 의미하게 됨. 더 복잡한 쿼리의 경우는 아래와 같이 @Query 어노테이션을 사용해 지정할 수 있음

짠~ 이렇게 따로 지정해줄 수 있음!

일단 따로 지정이 아닌 처음 작성한 findByUserId로 간다.

 

아래의 공식 사이트의 레퍼런스를 참고하여 메서드의 이름 작성 방법 및 예제를 확인할 수 있음!

https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation

 

 


1. 본격적으로 개발을 해 보자

퍼시스턴스 > 서비스 > 컨트롤러 순으로 구현

 

*@Sl4fj ; 로그 라이브러리~

 

1)Create Todo 구현 - Todo 생성

-퍼시스턴스 구현 ; save 메서드(엔티티 저장) / findByUserId 메서드(새 Todo 리스트 반환) > 모두 구현 완료

-서비스 구현 ;  

검증 > save 저장 > 리스트 반환 메서드 순으로 작성해주고

validation 부분은 계속해서 사용하므로 따로 빼내어 리팩토링 해주기

 

-컨트롤러 구현 ; 

create() 메소드 실행 후 리턴되는 엔티티를 담을 수 있도록 toEntity 메서드 작성
컨트롤러에 postmapping 어노테이션을 붙인 createTodo 메서드를 생성해줌

*메서드 및 설명...

*여기서 실행하려고 했더니 오류 발생... ( https://sum-milkyway.tistory.com/51 )

 

 

2)Retrieve Todo 구현 - Todo 검색

-퍼시스턴스 구현 ; TodoRepository 그대로 사용

-서비스 구현 ; retrieve() 메서드 작성

service 클래스에 Todo 검색하는 메서드 retrieve() 구현

-컨트롤러 구현 ; 컨트롤러의 @getmapping 이용,  retrieve() 메서드 작성

 

여기까지 작성하고 테스팅하기

-테스팅 ; postman 이용(H2 DB 사용으로 프로그램 재시작시 이전에 생성한 아이템은 삭제되므로, 다시 바로 아래에 있는 코드 작성-실행(POST)후 테스팅 코드 실행해줌

새로운 아이템 생성 코드
테스팅코드(GET)

 

3)Update Todo 구현 - Todo 검색

-퍼시스턴스 구현 ; TodoRepository 그대로 사용(save(), findByUserId() 사용)

-서비스 구현 ; update() 메서드 작성

optional & lambda 사용한 버전

 

-컨트롤러 구현 ; 컨트롤러의 @putmapping 이용(업데이트니까!!),  updateTodo() 메서드 작성

-테스팅 ; postman 이용(H2 DB 사용으로 프로그램 재시작시 이전에 생성한 아이템은 삭제되므로, retrievee()때와 같이 새로 생성 후 테스팅 코드 실행해줌

실행 후
이렇게 테스팅 코드 실행하면, 결과도 수정 데이터로 처리된 것을 볼 수 있음

 

4)Delete Todo 구현 - Todo 삭제

-퍼시스턴스 구현 ; TodoRepository 그대로 사용(delete(), findByUserId() 사용)

-서비스 구현 ; delete() 메서드 작성

 

-컨트롤러 구현 ; 컨트롤러의 @deletemapping 이용(delete니까!!),  deleteTodo() 메서드 작성

 

-테스팅 ; postman 이용(H2 DB 사용으로 프로그램 재시작시 이전에 생성한 아이템은 삭제되므로, retrievee()때와 같이 새로 생성 후 테스팅 코드 실행해줌

먼저 생성부터 해주고(post)
delete할 아이템의 userId만 있으면 되므로, id만 넣고 delete 메서드 실행해주면, 결과로 data가 텅~ 빈 걸 볼 수 있음!

 

 


프론트엔드

를 만져보자

 

0. 기본 이론

더보기

-Node.js ; 자바스크립트 런타임 환경. 브라우저 바깥에서도 실행할 수 있도록 함. 브라우저 밖에서도 실행 가능하다는 것은, 자바스크립트로 서버를 만들 수 있다는 것. 

-NPM ; Node.js의 패키지 관리 시스템. 

-브라우저의 동작 원리 ; 

 

 

1. 애플리케이션 생성/필요한 패키지 설치

react 워크스페이스로 이동(cd ~)후 npx로 리액트 애플리케이션을 생성해주어용

글고 material-ui 패키지를 설치(사이트 참고)

https://mui.com/material-ui/getting-started/installation/

 

2. 리액트 컴포넌트 작성

Todo.js 파일 생성 후, 다음과 같이 컴포넌트 작성
그리고 메인.. 즉 App.js에 작성한 Todo 를 넣어줍시다
그럼 이렇게 뜸!
Todo 컴포넌트를 하나 더 추가하여 재사용도 할 수 있다!

 

 

지금은 이렇게 임의로 지정한 이름이 정해진 똑같은 컴포넌트를 사용하고 있지만 이제는. 임의의 Todo 리스트+다른 타이틀을 가지고 있는 컴포넌트를 만들어 보기다.

 

Todo.js 에 새로운 내용 작성

더보기

; 필요한 매개변수를 생성자-constructor-를 통해 넘겨야줘야 하는데, 여기서는 constructor(props)~ 부분.

->super를 이용해 props 오브젝트를 초기화시키고, this.state*를 item변수와 props.item으로 초기화

  *state = 리액트가 관리하는 오브젝트. 추후에 변경할 수 있는 변수를 이 오브젝트에서 관리함(js 내에서 변경한 변수의 값을 HTML에 다시 렌더링하기 위해)

 

app.js

이제 props에 item 을 넘겨주기 위해 코드 작성해줌

더보기

이렇게 작성하면 생성자를 통해 props를 넘겨받고, this.state에서 item을 초기화 > this.state.item을 이용해 item 오브젝트에 접근 > Todo에 item={<변수>}를 이용해 props로 매개변수를 넘겨줌

 

암튼 지금은 

-id = 0

-title : Hello world1

-done : true(checked)

 

상태라, 실제로 실행해보면 이렇게 나옴

실행 결과

 

연습time~

1. Todo를 하나 더 만들고 상태값은 아래와 같음

-id = 0

-title : Hello world2

-done : false(unchecked)

2. todo를 배열 및 반복문을 통해 생성하고, 이 컴포넌트들을 넘기기

더보기

1) 일단 추가할 item을 items, 배열로 만들어 생성(초기화)

2)얘네를 배열 반복 해서 컴포넌트를 생성해줌

 3)이제 이걸 { } 에 넣어서 생성된 컴포넌트를 리턴시키기

 

 

실행 결과
전체 코드!

 

이제 이렇게 만들었으니... ui를 다듬어봅시다

material ui를 이용해서~~~!

 

todo.js

Todo를 요렇게 수정해주고

app.js

app.js 도 그에 맞춰 수정해줌

실행하면 이런 화면이!

실행 결과

 

이번에는 새로운 todo 아이템을 추가하고, 관련 화면을 만들어봄

AddTodo.js

UI부분이 되어줄 새 컴포넌트를 작성해주고

app.js

return 부분에 새로 작성한 AddTodo 컴포넌트를 삽입!

 

여기까지 마치고 실행하면 이렇게 UI가 완성되어있음!

실행화면

이제 이벤트 핸들러를 추가해서 기능을 넣어주면 됨...!!

UI만 갖추어진 상태라서 저기 + 버튼을 눌러도 아무런 일이 일어나지 않음~

 

추가해야 할 이벤트는 3가지.

1)onInputChange : 사용자가 input 필드에 키를 입력할 때마다 실행되고, 필드에 담긴 문자열을 js 오브젝트에 저장

2)onButtonClick : 사용자가 +버튼을 클릭할 때마다 실행되고, 1) 이벤트에서 저장하고 있던 문자열을 아래 리스트에 추가

3)enterKeyEventHandler : 사용자가 input 필드에서 엔터/리턴키를 눌렀을 때 실행되고, 기능은 2)와 같음(엔터키 사용으로 추가될 수 있도록 함)

 

이벤트1)  작성

1)onInputChange : 사용자가 input 필드에 키를 입력할 때마다 실행되고, 필드에 담긴 문자열을 js 오브젝트에 저장

더보기
AddTodo.js에 1)이벤트 추가

일단 onInputChange 함수 작성 후, TextField 쪽에 함수를 연결해줌

실행화면

그리고 실행하면, 이렇게 console 쪽에 textfield에 문자가 입력될 때마다 찍히는게 보임 > onInputChange() 가 정상 작동!

 

이벤트 2) 작성

2)onButtonClick : 사용자가 +버튼을 클릭할 때마다 실행되고, 1) 이벤트에서 저장하고 있던 문자열을 아래 리스트에 추가

더보기
add.js

app.js에 add 함수를 추가하고, 이 함수를 addTodo에 연결시켜줌

addTodo.js

윗쪽 생성자에 우선 넘겨받은 props를 this.add에 연결시켜주고

addtodo.js

그런 후에... onButtonClick 함수를 작성해서 넘겨받은 add함수를 사용하는 이벤트를 만들고, 실질적으로 이 이벤트가 동작하는 button에 이벤트를 연결시켜줌

실행화면

그리고 실행하면, todo 아이템을 리스트에 추가시킬 수 있다! 

 

이벤트 3) 작성

3)enterKeyEventHandler : 사용자가 input 필드에서 엔터/리턴키를 눌렀을 때 실행되고, 기능은 2)와 같음(엔터키 사용으로 추가될 수 있도록 함)

더보기
addtodo.js

enterKeyEventHandler 이벤트를 추가해주고, 이걸 onkeydown을 통해 textfield에 넣어 textfield에서 키가 눌릴때+enter키가 눌릴 때 실행되도록 함.

 

*책에서는 onKeyPress()를 사용하도록 안내하나, 지금은 deprecated되었으므로 다른 방법으로 대체함!

>>onkeydown을 통해 변경

 

실행결과

지금은 아무런 유효성 검사도 없지만... validation이 걸린다면 빈 title일 때 저장되는 걸 막을 수 있을 듯하다! 

 

 

이번에는 삭제 기능 구현.

-리스트의 요소마다 삭제 아이콘 추가

-삭제 아이콘 클릭 시 아이템 삭제 기능

 

todo.js

(*disableRipple 관련;  https://www.daleseo.com/material-ui-buttons/ )

 

먼저 listItemSecondaryAction 태그 작성하고, 

실행결과

실행해보면 이렇게 요소마다 휴지통 아이콘이 붙어있는걸 볼 수 있음!

 

app.js

그러고 나서... 이제 새로운 delete 함수를 작성하고, 이를 Todo에 연결시켜줌

 

todo.js

이제 생성자 쪽에 delete 추가해주고, deleteEventHandler 로 delete 함수까지 추가!

 

todo.js

그런 다음, 아이콘이 눌릴 때마다 delete 함수가 실행되어야 하므로, icon button에 onclick을 통해 함수를 연결시켜줌

 

실행화면

 

실행 후 아이콘 클릭을 통해 todo 아이템이 삭제됨!

 

마지막으로 수정부분...!!

타이틀 / 체크 박스 선택-해제 이렇게 두 개의 수정이 가능하게끔 기능을 추가할 것임

1)체크 박스 : item.done 부분을 true / false로 변경시켜주면 됨

더보기
todo.js

1)checkbox의 상태를 true-false 변환할 수 있는 함수 checkboxEventHandler() 를 작성하고, 

2)이걸 checkbox의 속성에 onclick / onchange를 통해 걸어줌

 

실행 화면

그럼 이렇게 정상적으로 잘~ 작동하는 걸 확인할 수 있음! 

 

2)타이틀 수정 : title 부분을 클릭하면 수정이 가능하고, enter 키를 누르면 수정 내용이 저장됨

더보기
todo.js

1)우선 readOnly라는 boolean형 상태 변수를 생성자에 추가하고, readonly상태를 변화시키는 함수를 추가

todo.js

2) 그 후 readOnly 상태를 연결하고, onclick을 통해 위에서 작성한 readOnly상태를 변화시키는 함수를 연결

>inputBase에는 inputProps라는 props가 있고, 거기에 readOnly가 있음(여기에는 state.readOnly 값이 들어감)

>타이틀이 클릭될 때마다 readOnly가 fasle로 바뀌어 수정이 가능한 상태로 변경되어야 하므로, onClick을 통해 readOnly 상태를 false로 변화시켜주는 offReadOnlyMode 함수가 실행되도록 걸어줌!

 

이 상태로 실행시켰을 경우

실행결과

타이틀을 클릭하면 커서가 뜨며 깜빡이는 것을 확인할 수 있음

3) 이번에는 ReadOnly를 true로 만드는, Enter키를 눌렀을 때 readOnly를 true로 바꾸는 함수를 작성

-새로운 함수 eventKeyHandler() 를 작성하고, 이를 input 필드의 onKeyDown 속성을 통해 작성

 

실행화면

정상작동을 확인하기 위해, console에 enter~handler 작동 시 true complete가 출력되도록 추가.

엔터키를 눌렀을 때, 콘솔창에서 정상 작동되는 것을 확인

 

todo.js

4) 이제 변경된 값을 저장해 줄 새로운 함수 edit~을 작성해주고, 작성한 값이 바뀔때마다 이 함수가 실행되어 변경된 값이 바로바로 변경될 수 있도록 onChange에 함수를 걸어줌

 

실행화면 - 수정 전
실행화면 - 수정 후

작성한 값을 수정하고, 마찬가지로 enter 키를 눌러 저장. enter 키를 누를 때 콘솔 창에 true complete가 뜨는 것을 확인 가능. 

 

여기까지 하면 title 수정 완료!

 

 

여기까지 하면 우선 프론트엔드 애플리케이션은 작성 완료. 했다

 


 

이제... 서비스 통합의 시간.

작성한 백엔드 / 프론트엔드 애플리케이션을 통합하여 하나의 웹 애플리케이션으로 만들어주자...

 

1)Todo 아이템을 불러오는 부분을 먼저 구현 - 도메인 접속 시 Todo 아이템이 리스트에 보이게끔!

→렌더링 직후 백엔드 API콜 > 결과를 리스트로 보여주기 위해 API 콜 부분을 componentDidMount 함수에 구현*

(*자세한 사항 https://sum-milkyway.tistory.com/61 참고)

더보기
App.js

1. 먼저 componentDidMount() 를 추가 - 콘솔창을 확인해보면

실행화면

이렇게 에러가 잔뜩 나와있는데...

이는 CORS* 헤더 policy를 위반했기 때문. (*https://sum-milkyway.tistory.com/64)

 

CORS가 가능하게끔 백엔드에서 방침 설정을 해주겠음!

 

2. 스프링으로 돌아가서,

스프링 - config 패키지 생성 후 새로운 클래스 webmvcconfig 생성

새로운 패키지에 새로운 클래스를 생성하고, 이렇게 코드 작성

코드는 결국 모든 경로에 대해 Origin이 http~3000인 경우, get~등과 같은 메서드를 이용한 요청을 허용하며, 모든 헤더와 인증에 관한 정보도 허용한다는 뜻.

 

이렇게 작성-재시작 후 브라우저 새고하면 

짠!

아까와는 다르게 정상적으로 접근(200)하여 연결된 것을 확인할 수 있음.

요청은 8080/todo로 갔고, 전과 같이 도메인이 다르나(localhost:3000) 잘 연결됨(200)

 

 

2) 이제 fetch를 이용해서, 애플리케이션이 사용할 백엔드 URI를 가져오도록 구현(도메인 변경 대비)

더보기
app-config.js

1. app-config.js 파일 생성 후, 다음과 같이 코드 작성

> 백엔드 서비스 주소 http://localhost:8000을 변수에 담고, 현재 브라우저의 도메인이 localhost인 경우 로컬 호스트에서 동작하는 백엔드 애플리케이션 사용.

*여기서 url을 잘못 설정하거나..오타를 낸다면 콘솔 창에서 해당 오류가 발생할 수도 있음(

오류...

(관련 게시글 참고 : https://sum-milkyway.tistory.com/67 )

 

service-ApiService.js

2. src-service 디렉터리 생성, 그 아래에 ApiService.js 생성 후 백엔드로 요청을 보낼 때 사용할 유틸리티 함수 작성

 (이렇게 작성하지 않으면 계속해서 콜 메서드를 매번 작성-실행해야 함)

 

App.js

3. ApiService.js에서 작성한 call 메서드로 기존 코드를 수정해줌

수정 후 실행 > Todo 아이템을 추가한 후 새로고침 / 아예 FE 애플리케이션을 껐다 켜도 을 해도 아이템은 사라지지 않음!

→백엔드의 DB에서 리스트를 가지고 오기 때문! 

 

 

2)Todo 아이템 수정 기능 > API를 이용해 update가 되도록, 서버 데이터 업데이트 후 변경된 내용을 화면에 재출력하는 과정 필요

더보기
app.js > update 함수 작성, Todo의 props에 연결
Todo.js > props 추가, enterKey~()/checkbox~() 함수에 백엔드 업데이트 부분 추가

1. update() 함수 작성, 연결

; 지금까지는 직접 eventHandler를 지정-추가했기 때문에 App.js에서 함수를 따로 추가하지 않아도 작동했으나, 이제 백엔드와 연결해야 하므로 이 과정이 필요함

; call() 을 통해 백엔드 API와 연결-기능 추가 후, update()를 새로 추가하여 백엔드와 연결.  

 

실행화면

새로고침 후 수정 기능을 사용해보면... 요청 메서드가 PUT(정상 작동 200)이 되었음을 확인할 수 있음

 

 


 

이제 인증 절차의 시간....

백엔드 인증 절차를 먼저 진행함.

(관련 이론 : https://sum-milkyway.tistory.com/68 )

 

1. User 레이어 구현

더보기

1. UserEntity 작성

persistence - UserPersistence

2. UserRepository 작성

Service - UsreService

3. UserService 작성 

dto - UserDTO
controller - UserController

4. UserController 작성 

 

여기까지 작성한 후 포스트맨을 통해 테스팅해봄!

 

회원가입 API를 이용한 계정 생성 완.
로그인 테스팅 성공!

잘~ 동작함을 확인할 수 있음. 

1. User 레이어 구현까지 완료하면 회원가입-로그인까지 잘 동작함을 알 수 있음. 

단, 지금까지의 과정에서는 

1) 로그인 상태 유지 불가 - 로그인 구현까진 완료되었지만, 다른 api는 사용자의 로그인 확인 불가(REST API는 상태가 없으므로 로그인 상태를 기억하지 않음)

2) 지금까지 작성한 API는 사용자의 로그인 여부 자체를 확인하지 않음 - 임의로 사용자 아이디를 작성해주었기 때문에,  지금 상태에서는 로그인 API를 사용하지 않음!

3) 패스워드를 암호화하지 않음

 

 

2. JWT 생성 및 반환 구현 > 지속적인 인증과 로그인 상태의 유지를 위해 구현

더보기

1. JWT 생성 및 반환 부분 구현

build.gradle

1) 일단 JWT 관련 라이브러리-jjwt- 디펜던시에 추가( https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt/0.9.1 )

security - TokenProvider

2) 인증과 인가를 위한 모든 클래스를 관리하는 security 패키지 생성,

이후 TokenProvider 클래스 생성 > 사용자 정보를 받아 JWT를 생성하는 클래스

UserController

3) 이제 이렇게~ User 레이어 구현 시 작성했던 코드 중 token 생성 및 반환과 관련된 부분에 security-부분의 메소드를 이용해 리펙토링 진행

 

*테스팅 도중 오류가 발생하여 dependencies 에 아래 코드 추가 - gradle refresh & restart 해주기

자세한 포스팅은 > https://sum-milkyway.tistory.com/69

build.gradle - dependencies

여기까지 하고 테스팅해보면,

실행화면 - 계정 생성
실행화면 - 로그인 요청 결과

 

짠~로그인 부분도 정상 작동됐고 토큰을 발행-로그인 시 반환되는 부분까지 구현 완료됨!

 

 

3. JWT를 이용한 인증 구현 > 스프링 시큐리티 이용

더보기
build.gradle

1) 스프링 시큐리티 디펜던시 추가 > gradle refersh 해주기

security -  JwtsAuthentication

2) 서블릿 필터 구현 > 토큰을 이용해 인증 - 인증정보 저장 - 인증된 사용자 저장

* OncePerRequestFilter ; 한 요청당 반드시 한 번만 실행됨

* 스프링 시큐리티의 SecurityContext ; createEmptyContext() 메서드 이용해 생성 - context에 인증정보인 authentication을 넣고 다시 securitycontextholder**에 context로 등록.

**기본적으로 ThreadLocal에 저장되며, Thread마다 하나의 context를 관리할 수 있으며 같은 Thread 내라면 어디에서든 접근 가능함

 

 

3) 서블릿 컨테이너에서 이 필터를 사용하라고 알려줌

 

ㅎㅎ

 

 

 

 

 

 

 

 

 

 

ㅎㅎ