2022.02.18
소셜로그인 oauth 구현을 담당했다.
Kakao 소셜로그인 과정에서, Section3 oauth 스프린트에서 했던 Github 로그인 부분을 참고해 백엔드와 소통하며 작성하는 중이다.
스프린트 클라이언트 부분은 클래스형으로 구현을 했었지만, 우리 프로젝트에서는 함수형 사용으로 정했기 때문에, 함수형으로 리팩토링 해봤다.
class Login extends Component {
constructor(props) {
super(props);
this.socialLoginHandler = this.socialLoginHandler.bind(this);
const REST_API_KEY = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const REDIRECT_URI = "http://localhost:3000/oauth/kakao";
this.KAKAO_LOGIN_URL = `https://kauth.kakao.com/oauth/authorize?client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&response_type=code`;
}
socialLoginHandler() {
window.location.assign(this.GITHUB_LOGIN_URL);
}
render() {
return (
...
)
}
}
export default function SignIn() {
const REST_API_KEY = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const REDIRECT_URI = "http://localhost:3000/oauth/kakao";
const KAKAO_LOGIN_URL = `https://kauth.kakao.com/oauth/authorize?client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&response_type=code`;
const socialLoginHandler = () => {
window.location.assign(KAKAO_LOGIN_URL);
};
return (
...
)
}
//순서대로 작성하다 보면 socialLoginHandler 앞에 function/const를 붙여봐도, 아무것도 안붙여도 빨간밑줄이 없어지지 않는다.
//return 부분에 socialLoginHandler를 사용하게 되면 그제서야 빨간줄이 사라진다.
구글링을 해봐도 bind의 역할이 잘 이해되지 않았는데 다음 글에서 조금 해답을 얻었다.
https://www.popit.kr/그-많던-bind-함수는-다-어디로-갔을까/
App.js
class App extends Component {
constructor() {
super();
this.state = { isLogin: false, accessToken: "" };
this.getAccessToken = this.getAccessToken.bind(this);
}
async getAccessToken(code) {
const resp = await axios.get(
`http://localhost:8080/oauth/kakao/callback?code=${code}`,
{ authorizationCode: code }
);
// post 사용하고 싶으면 다음과 같이 쓰면 된다.
// axios.post(`http://localhost:8080/oauth/kakao/callback?code=${code}`)
✅ 백엔드 http://localhost:8080/oauth/kakao/callback 주소로부터 accessToken을 받아오므로 필요한 코드이다.
server/index.js에 다음과 같이 적혀있다:
app.get("/oauth/kakao/callback", handleCallback);
this.setState({
isLogin: true,
accessToken: resp.data.accessToken,
});
}
componentDidMount() {
const url = new URL(window.location.href);
const code = url.searchParams.get("code");
✅ 여기 code가 kakao에서 받아오는 인가코드.
// ex) http://localhost:3000/?code=5e52fb85d6a1ed46a51f
if (code) {
this.getAccessToken(code);
}
}
// MyPage에게 accessToken을 props로 넘겨주기 (로그인 완료 후 홈으로 리디렉션되끔 리팩토링 예정)
render() {
const { isLogin, accessToken } = this.state;
return (
<Router>
<div className="App">
{isLogin ? <MyPage accessToken={accessToken} /> : <Login />}
</div>
</Router>
);
}
}
export default App;
window.location.href
를 쳐보면 된다.new URL (window.location.href)
를 쳐보면 인가코드가 search 안에 들어있는 걸 볼 수 있다.(componentDidMount() 부분을 useEffect 사용하고, 전체를 함수형 컴포넌트로 바꿔봤는데, 작동방식이 조금 이상하다. 수정할 예정.)
function App() {
const [isLogin, setIsLogin] = useState({ isLogin: false, accessToken: "" });
async function getAccessToken(code) {
const response = await axios
.get({
method: "GET",
url: `http://localhost:8080/oauth/kakao/callback?code=${code}`,
})
.then((res) => {
console.log(res);
});
console.log(response);
// setIsLogin({ isLogin: true, accessToken: resp.data.accessToken });
}
useEffect(() => {
let code = new URL(window.location.href).searchParams.get("code");
if (code) {
getAccessToken(code);
}
}, []);
return (
<Router>
<div className="App">
{isLogin.isLogin ? (
<Mypage accessToken={isLogin.accessToken} />
) : (
<Login />
)}
</div>
</Router>
);
}
서버 부분 callback.js를 구현하지 않았을 때 뜨는 에러다.
server/callback.js 를 다음과 같이 작성하면 정보 전달이 잘 된다.
module.exports = async (req, res) => {
// 스프린트 때엔 req의 body로 authorization code가 들어왔지만, 지금은 req.query로 인가코드가 들어온다.
// console.log("쿼리는", req.query);
const authorizationCode = req.query.code;
// authorizationCode를 카카오에게 보내고 accessToken을 받아오기.
const token = await axios.post(
`https://kauth.kakao.com/oauth/token?grant_type=authorization_code&client_id=ABCDEFGHIJK&redirect_uri=http://localhost:3000/oauth/kakao&code=${authorizationCode}`
);
// console.log("토큰은", token.data);
// 받은 access_token을 header에 담아 https://kapi.kakao.com/v2/user/me 경로로 보내면 사용자 정보를 받게 되는 과정
const userInfo = await axios.get("https://kapi.kakao.com/v2/user/me", {
headers: { Authorization: `Bearer ${token.data.access_token}` },
});
console.log(userInfo.data);
// 이제 우리 사이트 전용토큰 만들어서 클라이언트에게 사용자 정보와 함께 넘기면 된다.
};
맨 마지막 console.log(userInfo)를 치면 서버 터미널에서 엄청 긴 객체가 찍혀 들어온다.
그 객체의 맨 밑에 data : { 로 시작하는 부분이 있는데, 그 안에 사용자 정보가 있다는 걸 알게 되었다.
그래서 console.log(userInfo.data)라고 쳐보면 다음과 같이 예쁘게 나온다.
{
id: 1234567,
connected_at: '2022-02-18T05:42:55Z',
properties: { nickname: 'jenjenny' },
kakao_account: {
profile_nickname_needs_agreement: false,
profile_image_needs_agreement: true,
profile: { nickname: 'jenjenny' },
has_birthday: true,
birthday_needs_agreement: true
}
}
kakao_account 안의 내용은 사용자 동의항목이다. 내가 REST API 설정하는 카카오 사이트에서 동의항목 설정해놓은대로 사용자가 다음과 같은 화면을 보게 되고, 동의하면 정보가 우리에게 제공된다.
https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api
Kakao Developers 문서에 잘 정리되어 있지만,
다음 블로그에서 이해하기 쉽게 설명해 놓아서 도움을 많이 받았다.
https://data-jj.tistory.com/53
https://2dowon.netlify.app/react/social_login/
https://velog.io/@seize/React-카카오-소셜-로그인 (JavaScript SDK는 REST API용과 다른 거라서 흐름만 참고했다.)