Aspect Oriented Programming 관점지향 프로그래밍
(객체지향프로그래밍과 같이 쓰겠다는 말임)
로그인 기능과 회원가입기능을 구현한다고 했을 때,
각 기능들의 핵심기능(노란색부분)은 다른데 그외에 앞단과 뒷단의 처리는 같다. 이때, 이 기능들을 공통기능(파란색부분)이라고 한다.
핵심기능과 공통기능을 분리, 공통기능은 필터링처리하고 핵심기능은 코드(컨트롤로, 서비스 안)로 짠다. >> 공통기능들은 AOP처리 하고 핵심기능들은 코드로 적는다.
그렇게 하면, 코드 안에 유효성검사 등등이 없어도 되기 때문에 코드가 깔끔해진다.
1) AOP 만들기
(1) 라이브러리 설치
handler.aop 패키지 생성 후, ValidationAdvice 클래스 만들기
(2) 공통기능 넣기
8줄 : 메모리를 띄우기 위해, @Component 사용 >> 어디에 해당하는지 모를때(RestController, Service 등은 모두 Component를 상속해서 만들어진 것이다.)
9줄 : @Aspect >> AOP 처리할 수 있는 핸들러로 만들어준다.
10줄 : Advice 는 공통기능을 의미함
12,19줄 :
어떤 특정함수가 실행될 때, (예:profile()) apiAdvice() 함수 내부가 먼저 실행이 되고, 리턴할 때 profile() 함수가 실행된다.
16~18줄(28~30줄) : 함수의 매개변수에 접근해서 매개변수를 뽑아 출력해본다.
이때, JsonIgnoreProperties로는 무한참조를 막지 못해서 에러가 나기 때문에 User.java 아래와 같이 해준다. (Generate toString 후에 images를 뺌)
페이지에서 댓글을 쓰는 기능의 컨트롤러는 3개의 매개변수를 가진다.
그 3개의 매개변수를 뽑아 출력되는 것을 알 수 있음
19~20줄 : BindingResultType이라는게 있으면, 20줄의 내용을 출력한다.
가입하기를 해보면, web컨트롤러에서 유효성 검사를 한다.
(3) AOP 만들기
27줄 : arg를 BindingResult로 다운캐스팅을 해야한다.
29~37줄 : commentSave에 있는 유효성검사 하는 코드를 넣는다.
(참고! 36줄의 throw를 날리는 순간 밑의 코드들은 무효화된다)
apiController 쪽 함수들 내부의 if문을 다 없애도 작동하게 된다. >> AOP
((결과))
((이전))
코드가 엄청 간단해지고 깔끔해짐! 큰 프로젝트일 때는 이 방법이 매우 효율적이다.
Controller의 AOP도 만들어준다.
52~60줄 : AuthController에서 if문을 가져와서 넣어준다.
59줄 : CustomVaildationException 이다. (api 없음)
깔----끔
앞으로 유효성 검사가 필요한 곳에는
1) 앱 등록 세팅
(1)
pom.xml에 auth2라는 라이브러리가 필요하다.
Spring boot Starter에서 만들때 선택해서도 가능하다.
그리고 페이스북개발자사이트(https://developers.facebook.com/)에서 '내앱만들기'를 한다.
웹 선택
도메인 없기 때문에 로컬호스트로 넣는다.
여기 나온 앱ID와 앱시크릿코드를 스프링부트에 등록을 해줘야 한다.
application.yml에 oauth2를 추가해준다.
42줄 : registration에서 엔터 한번 탭 한번 눌러서 시작하면되는데, 여기에는 우리가 로그인할, Open auth를 제공해주는 회사명(변수)을 쓰면 된다.
43줄 : (다시 엔터,탭 한번씩 한 후) client-id:에 앱ID를 한칸 띄고 적는다.
44줄 : 앱시크릿 코드를 넣는다.
45줄 : scope는 어떤 데이터를 받을지에 대한 범위를 정하는 것(문서를 봐야함)
제공하는 회사마다 다르기 때문에 항상 문서를 보고 적어줘야 한다.
(2) OAuth 원리
OAuth는 Open Auth라고 한다. 인증을 오픈하겠다라는 의미이다.
유저1이 페이스북에서 회원가입을 하게 되면, 페이스북은 DB에 해당 유저의 회원정보를 가지고 있다.
유저1이 사이트A에 접속을 하면 회원가입창이 뜨는데 유저1이 해당 사이트에 가입하지 않고 로그인으로 가서 페이스북 로그인을 하겠다고 했을 때, 일어나는 과정은 다음과 같다.
① 페이스북 로그인
② 유저1이 입력한 ID와 PW(예: ssar/1234), 사이트 정보를 페이스북에게 전송
③ 페이스북은 사이트A가 앱리스트에 등록된 사이트인지를 확인
④ 앱리스트의 등록이 확인되면, 페이스북 DB의 User 테이블에서 회원정보 확인
⑤ 페이스북쪽에서 인증이 완료되고, 사이트 A에게 인증코드를 돌려줌 >> 페이스북이 인증된 사용자라고 공인해주는 것.
사이트가 앱리스트에 꼭 등록이 되어 있어야 한다.
사이트는 인증된 유저의 정보가 필요하다.
인증코드를 받으면서 엑세스토큰을 받은 사이트A는 유저의 정보를 받을 수 있으나 그 범위가 정해져 있다. 그 범위를 Scope라고 하고 그 안에 담긴 정보(email, public profile)만 받을 수 있다. 이 정보를 토대로 사이트A는 유저1을 강제 회원가입 & 로그인을 시킨다.
※ 엑세스토큰 : 사이트에서는 인증된 유저의 정보에 접근할 수 있는 권한대행
위에서 설정한 auth2라는 라이브러리는 위에서 설명한 과정이 내부적으로 구현이 되어있어 코드를 받고 엑세스토큰을 받을 필요가 없다. 회원정보(email, public profile)를 바로 받을 수 있다. 그리고 코드 구현 과정은 생략 가능하다.
이때 받은 회원정보로 회원가입/로그인 시키면 된다.
2) 페이스북 연동
(1) 시큐리티 셋팅
SecutiryConfig
32줄 : form로그인도 하는데, oauth2로그인도 할 것이다.
33줄 : oauth2로그인을 하면 최종응답을 코드로 하지 않고 회원정보를 바로 받는다.
34줄 : 회원정보 받는 서비스를 oAuth2DetailsService로 한다. 이때, 서비스에서 DefaultOAuth2UserService를 상속받아서 타입을 맞춰줘야 한다.(타입을 맞춰주지 않으면 에러가 난다.)
Service 참고
(2) 화면
signin.jsp
44줄 : /oauth2/authorization/ 은 키값으로 고정이다. 마지막에 들어가는 yml에서 설정한 변수값(회사명)으로 구분된다.
/oauth2/authorization/facebook >> 이 주소는 뭘까? 페이스북 로그인으로 연결되게 해주는 스프링부트의 기본설정 (SpringBoot tutorial에 있음)
(3) Service
페이스북 로그인 창에서 데이터를 입력 후 로그인 버튼을 클릭하면 페이스북으로 데이터가 전송되고 응답한다.
그 응답을 처리하는 곳 >> OAuth2DetailsService
13줄 : 응답을 해주면 loadUser() 함수가 실행된다. userRequest 안에 유저 정보가 있다.
17줄 : 응답정보를 통해서(파싱해서) 정보를 oauth2User에 String으로 담는다.
Scope가 제공하는 정보에 대한 동의 창이 뜨고, 동의 후 로그인하면 아래와 같이 유저정보를 받아오는 것을 확인할 수 있다.
이 정보를 가지고 회원가입을 진행하기 위해서는 UserRepository가 필요하다. (22줄)
31줄 : 유저정보를 확인했던 oauth2User.getAttributes()의 리턴타입이 Map타입이므로 Map으로 받는다.
33줄 : username은 제공한 정보에 없기 때문에 강제로 지정해준다. (페이스북 쪽으로 로그인을 하기 때문에 중요하진 않지만 중복은 불가함)
facebook_(페이스북 로그인 사용자)+ id값(페이스북의 고유한 값, 충돌날 일 없음)
34줄 : 암호화와 UUID를 이용해서 랜덤으로 가져온 값을 String으로 만들어서 넣는다. (패스워드로 로그인할 게 아니기때문에 패스워드는 몰라도 된다.)
35,6줄 : Hash Map에서 정보를 꺼낼 때는 get("오브젝트 key")로 가져와서 String 변수에 넣는다. 이때, 뽑아온 값의 타입은 오브젝트이므로 String으로 다운캐스팅한다.
38~44줄 : user 정보를 만든다. dto에 들어가는 필수값(username, password, email, name, role)을 넣어준다.
페이스북으로 로그인 이후에 또 로그인 하면 계속 저장(INSERT)되면 매우 비효율적이다. >> 회원정보가 이미 있는 유저인지를 확인하는 과정 필요함
.
OAuth2DetailsService에서 세션에 리턴하도록 할 때, 세션에 정보를 저장해서 리턴을 하는데 이때 리턴 타입의 구분을 없게 하기 위해 OAuth2User를 implements 한다. (PrincipalDeatails)
그렇게 되면, principalDeatails 타입으로 자유롭게 꺼낼 쓸 수 있다.
로그인 방법에 따른 데이터 사이의 구분을 하기 위해서는 아래처럼 하면 된다.
여기서 attributes는 {id=102831402124555, name=ssar, email= ssar@gmail.com} 이걸 의미한다.
나중에 OAuth로 로그인때는 28~30줄의 생성자를 때리게 된다.
.
42~51줄 : 최초 로그인을 할 때(userEntity 값이 null)는 저장을 하고 그 값을 세션에 담아 리턴한다.
53~55줄 : null 값이 아니면 최초 로그인이 아니고 페이스북으로 이미 회원가입이 되어 있음을 의미. 세션에 담아 리턴한다.
50,55줄 표시한 부분: oauth 로그인인지를 구분할 필요가 있을 때 사용한다.
이대로 실행하면 에러가 뜨는데 username이 20자보다 길기 때문이다. OAuth2 로그인을 위해 칼럼의 길이를 100자로 늘린다.
배포는 나중에...!
응용해서 다른 프로젝트도 해보기