OAuth (Open Authorization)는 웹사이트나 애플리케이션이 사용자의 비밀번호를 공유하지 않고도 다른 애플리케이션 또는 웹사이트에 제한된 자원 접근 권한을 부여할 수 있도록 하는 개방형 표준으로 즉, OAuth는 안전한 위임(access delegation)을 위한 프로토콜 규약이다.
주요 개념
- 자원 소유자 (Resource Owner): 자원을 소유한 사용자입니다. 예를 들어, 사용자가 Google Drive에 저장된 파일을 가지고 있다면, 사용자가 자원 소유자
- 클라이언트 (Client): 자원 소유자가 접근을 허락한 제3의 애플리케이션입니다. 예를 들어, 사용자가 자신의 Google Drive 파일에 접근하도록 허용한 웹 애플리케이션이 클라이언트
- 자원 서버 (Resource Server): 보호된 자원이 저장된 서버입니다. Google Drive 서버가 그 예
- 인증 서버 (Authorization Server): 자원 소유자가 자원 접근 권한을 클라이언트에게 부여하기 위해 사용되는 서버로, 이 서버는 클라이언트에게 액세스 토큰을 발급
- 액세스 토큰 (Access Token): 클라이언트가 자원 서버에 접근하기 위해 사용하는 토큰으로, 이는 제한된 시간 동안 유효하며, 특정 자원에 대한 접근 권한을 나타낸다.
카카오 로그인 OAuth 흐름
public void kakaoLogin(String code) throws JsonProcessingException {
// 1. "인가 코드"로 "액세스 토큰" 요청
String accessToken = getAccessToken(code);
// 2. "액세스 토큰"으로 "카카오 사용자 정보" 가져오기
KakaoUserInfoDto kakaoUserInfo = getKakaoUserInfo(accessToken);
// 3. "카카오 사용자 정보"로 필요시 회원가입
Users kakaoUser = registerKakaoUserIfNeeded(kakaoUserInfo);
// 4. 강제 로그인 처리
forceLogin(kakaoUser);
}
- 사용자 인증: 사용자가 클라이언트 애플리케이션에서 자신의 계정으로 로그인
- 권한 부여 요청: 클라이언트는 인증 서버에 사용자의 자원에 대한 접근 권한을 요청
- 사용자 동의: 사용자는 클라이언트에게 특정 자원에 대한 접근 권한을 부여할지 여부를 결정
- 권한 부여 코드 발급: 사용자가 동의하면, 인증 서버는 클라이언트에게 권한 부여 코드를 발급
private String getAccessToken(String code) throws JsonProcessingException {
// HTTP Header 생성
HttpHeaders headers = new HttpHeaders();
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
// HTTP Body 생성
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("grant_type", "authorization_code");
body.add("client_id", "클라이언트ID");
body.add("redirect_uri", "http://lmshi.shop:8083/user/kakao/callback");
body.add("code", code);
// HTTP 요청 보내기
HttpEntity<MultiValueMap<String, String>> kakaoTokenRequest =
new HttpEntity<>(body, headers);
RestTemplate rt = new RestTemplate();
ResponseEntity<String> response = rt.exchange(
"https://kauth.kakao.com/oauth/token",
HttpMethod.POST,
kakaoTokenRequest,
String.class
);
// HTTP 응답 (JSON) -> 액세스 토큰 파싱
String responseBody = response.getBody();
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(responseBody);
return jsonNode.get("access_token").asText();
}
- 액세스 토큰 요청: 클라이언트는 권한 부여 코드를 이용해 인증 서버에서 액세스 토큰을 요청
- 액세스 토큰 발급: 인증 서버는 클라이언트에게 액세스 토큰을 발급
- 자원 서버 접근: 클라이언트는 액세스 토큰을 사용해 자원 서버에서 자원을 요청
private KakaoUserInfoDto getKakaoUserInfo(String accessToken) throws JsonProcessingException {
// HTTP Header 생성
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + accessToken);
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
// HTTP 요청 보내기
HttpEntity<MultiValueMap<String, String>> kakaoUserInfoRequest = new HttpEntity<>(headers);
RestTemplate rt = new RestTemplate();
ResponseEntity<String> response = rt.exchange(
"https://kapi.kakao.com/v2/user/me",
HttpMethod.POST,
kakaoUserInfoRequest,
String.class
);
String responseBody = response.getBody();
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(responseBody);
Long id = jsonNode.get("id").asLong();
String nickname = jsonNode.get("properties")
.get("nickname").asText();
String email = jsonNode.get("kakao_account")
.get("email").asText();
return new KakaoUserInfoDto(id, nickname, email);
}
자원 제공: 자원 서버는 액세스 토큰을 검증한 후 클라이언트에게 요청된 자원을 제공하고 서버에서 중복체크 후 회원가입후 로그인 처리한다.
private Users registerKakaoUserIfNeeded(KakaoUserInfoDto kakaoUserInfo) {
// DB 에 중복된 Kakao Id 가 있는지 확인
Long kakaoId = kakaoUserInfo.getId();
Users kakaoUser = userRepository.findByKakaoId(kakaoId)
.orElse(null);
if (kakaoUser == null) {
String kakaoEmail = kakaoUserInfo.getEmail();
Users sameEmailUser = userRepository.findByEmail(kakaoEmail).orElse(null);
if(sameEmailUser != null){
kakaoUser = sameEmailUser;
// 기존 회원 정보에 카카오 ID 추가
kakaoUser.setKakaoId(kakaoId);
}else{
//신규회원 가입
// user name 카카오 닉네임
String nickname = kakaoUserInfo.getNickname();
// password : random UUID
String password = UUID.randomUUID().toString();
String encodedPassword = passwordEncoder.encode(password);
// email : kakao email
String email = kakaoUserInfo.getEmail();
// role : 일반 사용자
UserRoleEnum role = UserRoleEnum.USER;
kakaoUser = new Users(nickname, encodedPassword, email, role, kakaoId);
}
userRepository.save(kakaoUser);
}
return kakaoUser;
}
private void forceLogin(Users kakaoUser) {
UserDetails userDetails = new UserDetailsImpl(kakaoUser);
Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
OAuth는 특히 소셜 로그인 기능을 구현할 때 많이 사용되며, Google, Facebook, Twitter, Naver, Kakao 등 다양한 서비스에서 표준적으로 채택하고 있으며, 이를 통해 사용자는 비밀번호를 공유하지 않고도 다른 애플리케이션과 안전하게 데이터를 공유할 수 있다.
위의 과정을 프론트 코드로 구현해보면
카카오에서 발급받은 클라이언트 아이디를 발급하고, 해당 url을 등록해야 한다.
<button id="login-kakao-btn" onclick="location.href='https://kauth.kakao.com/oauth/authorize?client_id=클라이언트아이디&redirect_uri=http://http://lmshi.shop:8083/user/kakao/callback&response_type=code'">
카카오로 로그인하기
</button>
SSR
SSR (Server-Side Rendering)는 웹 애플리케이션의 HTML을 서버 측에서 렌더링하여 클라이언트(브라우저)로 전달하는 기술입니다. 이는 클라이언트 측에서 자바스크립트가 실행되기 전에 완전한 HTML을 제공하여 초기 로드 시간을 단축하고 SEO(검색 엔진 최적화)를 향상시키는 데 유용합니다.
SSR의 주요 특징 및 이점
- 빠른 초기 로드: 서버에서 미리 렌더링된 HTML을 클라이언트에 제공하므로, 클라이언트가 페이지를 로드할 때 초기 콘텐츠를 빠르게 볼 수 있습니다. 이는 사용자 경험을 크게 향상시킵니다.
- SEO 향상: 검색 엔진 크롤러는 자바스크립트를 실행하지 않거나 제한적으로 실행하기 때문에, 서버에서 미리 렌더링된 HTML은 크롤러가 콘텐츠를 더 쉽게 인덱싱할 수 있게 합니다. 이는 검색 엔진 최적화를 개선하는 데 도움을 줍니다.
- 더 나은 성능: 클라이언트가 서버에서 이미 렌더링된 HTML을 수신하면, 브라우저는 이를 바로 표시할 수 있습니다. 이는 특히 네트워크 속도가 느리거나 클라이언트 장치의 성능이 낮을 때 유용합니다.
SSR의 단점
- 서버 부하 증가: 모든 요청마다 서버가 HTML을 렌더링해야 하기 때문에 서버의 부하가 증가할 수 있습니다. 이는 고성능 서버가 필요하게 만들 수 있습니다.
- 복잡성 증가: 클라이언트 측에서만 렌더링하는 것보다 SSR을 구현하고 유지 관리하는 것이 더 복잡할 수 있습니다. 서버와 클라이언트 양쪽에서 코드가 실행되어야 하기 때문입니다.
SSR의 사용 사례
- Next.js: React 애플리케이션을 위한 프레임워크로, 서버 사이드 렌더링을 쉽게 구현할 수 있게 해줍니다.
- Nuxt.js: Vue.js 애플리케이션을 위한 프레임워크로, 역시 서버 사이드 렌더링을 지원합니다.
- Angular Universal: Angular 애플리케이션에서 SSR을 가능하게 해줍니다
SSR vs CSR (Client-Side Rendering)
- CSR (Client-Side Rendering): 클라이언트 측에서 자바스크립트를 사용하여 HTML을 렌더링하는 방식입니다. 일반적으로 초기 로드 속도는 느리지만, 페이지 전환이 빠르고 부드럽습니다.
- SSR: 서버 측에서 HTML을 미리 렌더링하여 클라이언트에 제공하는 방식입니다. 초기 로드 속도가 빠르고, SEO에 유리합니다.
SSR은 초기 로딩 속도를 개선하고 SEO를 강화하는 데 유리하지만, 서버에 더 많은 부하를 주고 구현 복잡성이 증가할 수 있습니다. 반면, CSR은 동적 인터랙션이 많은 애플리케이션에서 유리합니다. 두 가지 접근 방식은 각각의 장단점이 있으므로, 애플리케이션의 요구 사항에 따라 적절한 방식을 선택하는 것이 중요합니다.
// pages/index.js
import React from 'react';
const Home = ({ data }) => {
return (
<div>
<h1>Server-Side Rendering Example</h1>
<p>{data.message}</p>
</div>
);
};
// getServerSideProps 함수는 요청 시마다 호출되어 데이터를 미리 가져옵니다.
export async function getServerSideProps() {
// 예제 데이터를 가져오는 부분 (API 호출 등)
const data = { message: 'This is rendered on the server side' };
// props를 반환하여 페이지 컴포넌트에 전달
return {
props: { data },
};
}
export default Home;
'4차산업혁명의 일꾼 > 웹개발' 카테고리의 다른 글
자바8 , 11, 17 간단 정리 (6) | 2024.06.18 |
---|---|
스프링 버전 4와 5의 차이 정리 (2) | 2024.06.18 |
log와 junit5 ,validation 그리고 interceptor (2) | 2024.06.17 |
java 파일 다운로드/ 엑셀및 pdf 그리고 소켓통신 그리고 JSON 복습 (0) | 2024.06.17 |
javascript/jsp및 프론트 복습 (0) | 2024.06.17 |