항해99 5주차 WIL

오세명·2021년 10월 17일
0

본격적으로 기억과 감정이 아닌 하드스킬에 관한 부분을 기록하기 위해 WIL의 틀을 바꾸어봅니다.

첫 협업을 진행하면서 느꼈던 점

  1. 백엔드(Java, Spring)과 프론트엔드(Javascript, React)간의 언어가 달라 소통에 어려움이 있었다. 클라이언트의 경우, res.json으로 보내주세요 라고 이야기를 한다면 스프링 선생님들은 이를 이해하지 못한다. 원활한 소통을 위하여 그들의 언어를 미약하게나마 공부해야겠다는 생각이 들었다.

  2. API 설계와 와이어 프레임은 반드시 탄탄하게 잡아야 한다. API 설계는 백엔드 개발자와 프론트엔드 개발자와의 약속이며 와이어 프레임은 프론트엔드 개발자와의 약속이다. 실제 개발은 이 약속대로 진행하면 된다. 중간에 변수가 생겨 바뀔지언정, 그 틀 안에서 바뀌는 것이기 때문에 유지 보수에도 용이하다. 그런데 이 과정이 탄탄하지 않는다면? 시간은 없는데 뭐를 받아서 UI를 그려주는지 모른다면? 정말로 힘들 것이다. 따라서 그 틀을 주관적이지만 비즈니스 로직과 일치하게 만들어 놓은다면, 모두가 하나의 추상적인 공간을 공유하고 있게 되므로, 코드를 더 빨리 작성할 수 있을 것이다.

  3. 나는 프론트 엔드 개발자에게 필요한 소양이 UI를 이쁘게 그리는 것에 국한된 것이라고 생각하지 않는다. 사용자가 편할 정도의 UI와, 그것보다 더 나은 UX를 제공하기 위해 로직을 구현하는 것이 진정 프론트엔드 개발자가 가져야할 소양이라고 생각한다. 최근 연이은 CSS 압박으로 많은 스트레스를 받았다. 내가 가지고 있는 장점은 UI적인 세심함보다는 UX와 로직에 대한 세심함이라고 생각하는데, 자꾸 그 장점이 단점으로 여겨져 자존감도 많이 떨어져 있다. 그렇다고 하던 것을 멈추고 CSS만 공부한다면 그것도 말이 안되지 않겠는가.. 하루에 조금이라도 UI 컴포넌트를 이쁘게 그리는 연습을 해보아야겠다. 어차피 둘 다 필요하다면 조금씩 하는게 안하는 것 보다 나으니깐 말이다.

  4. 더 많은 예외처리에 신경을 써야겠다. 스프링 선생님들께서 예외 처리를 어떤 형태로 하시는지 알지 못하여 이에 관한 토의를 해본 적이 없지만, Node는 예외에 대하여 throw로 던지므로 catch로 받아 원하는 렌더링 로직을 더 짤 수 있을 것이다. 다음 주에는 이런 예외처리를 목표로 구현해보아야겠다.

내가 중점으로 두었던 것

1. JWT를 이용한 로그인 구현

  • 유저가 진짜 유저임을 검증하는 방법은 여러 가지가 있는데, 이번 프로젝트에서는 JWT를 이용하였다.
  • 클라이언트의 로그인 페이지에서 POST를 보내면 서버는 AccessToken을 발급해준다.
  • 이 AccessToken을 통하여 서버가 '진짜 유저'임을 구별해내는 것이 중요하다.
  • 클라이언트가 해야하는 일은 AccessToken을 안전하게 보관하는 것, 그리고 서비스 중 '로그인을 해야만 하는' 로직을 잘 구현하는 것이다.
  • 리서치를 한 결과, RefreshToken은 쿠키에, AccessToken은 Request Header에 저장시켜 관리를 하는게 일반적인 방법인 듯 하다.
  • 그러나 쿠키 역시 보안상의 노출 위험이 있으므로, 방어하는 로직을 더 찾아보아야겠다.
  • 내가 사용했던 토큰 방식
// 토큰을 헤더에 저장하는 axios 로직

import axios from 'axios';
import { getToken } from '../utils/auth';

const baseURL = process.env.REACT_APP_REMOTE_SERVER_URI;

const instance = axios.create({ baseURL });

const setToken = (config) => {
  config.headers['Content-Type'] = 'application/json; charset=utf-8';
  config.headers['X-AUTH-TOKEN'] = `${getToken()}`;
  config.headers.withCredentials = true;
  return config;
};

instance.interceptors.request.use(setToken);

export default {
  GET: (endpoint, additionalPath) =>
    instance.get(`${endpoint}${additionalPath ? `/${additionalPath}` : ''}`),
  POST: (endpoint, body) => instance.post(endpoint, body),
  UPDATE: (endpoint, urlParam, body) =>
    instance.post(`${endpoint}/${urlParam}`, body),
  DELETE: (endpoint, urlParam) => instance.delete(`${endpoint}/${urlParam}`),
};
// 유저의 로그인 유무 관련 로직
import React from 'react';
import { Redirect } from 'react-router-dom';
import PropTypes from 'prop-types';
import Loading from './Loading';
import { useUserAuthentication } from '../hooks';

const Permit = ({ children }) => {
  const [isUserAuthorized, token] = useUserAuthentication();

  if (token && !isUserAuthorized) {
    return <Loading />;
  }

  if (!(token || isUserAuthorized)) {
    return <Redirect to="/sign" />;
  }

  return children;
};

Permit.propTypes = {
  children: PropTypes.element.isRequired,
};

export default Permit;

2. Validation 로직 설계

  • 커스텀 훅으로 관리하고, type을 인자에 전달하여 어떤 타입인지에 따라 validation을 실시한다.
  • validation은 잘못된 정보를 사용자가 입력했을 때, API Call이 일어나지 않도록 방어하는 마지막 보루라고 생각하고 열심히 작성해보았다.
  • 작성한 로직을 다시 복기해보니, 마치 유저가 아무것도 입력하지 않았는데 validated된 것 처럼 느껴진다. 변수 명에 조금 더 신경을 써야겠다.
import React from 'react';
import { validateEmail, matchPasswordLength } from '../utils';

export default function useValidation(type, initialValue) {
  const [state, setState] = React.useState(
    typeof initialValue === 'function' ? initialValue() : initialValue,
  );

  const [isStateValid, setIsStateValid] = React.useState(true);

  const onChange = React.useCallback((nextState) => {
    const value =
      typeof nextState === 'function' ? nextState(state) : nextState;

    setState(value);

    if (value.length === 0) {
      setIsStateValid(true); // input값이 아무것도 없다면 초기화한다.
      return;
    }

    if (type === 'email') {
      setIsStateValid(validateEmail(value));
      return;
    }

    if (type === 'password') {
      setIsStateValid(matchPasswordLength(value));
    }
  }, []);

  return [state, isStateValid, onChange];
}

3. Redirection을 통한 제3자 인증 방법

  • 먼저 해당 기업의 서비스에서 제3자인증을 어떤 식으로 제공하고 있는지 찾아본다.
  • Redirection으로 제공한다면 인가코드를 반드시 클라이언트 스코프에서 받을 수 있도록 로직을 설계한다.
    • 반드시 window.location.href으로 던져줄 필요가 있는가? 이에 대하여 리서치할 것
  • 백엔드에서 서버에 인가 코드를 요청할 때 리다이렉트하는 주소가 명시된 규약대로 되었는지 반드시 체크할 것
// useLocation 사용

  React.useEffect(() => {
    if (!location.search) {
      return null;
    }
    
    const query = location.search;
    const code = query.split('=')[1];

    async function handleKaKaoLoginRedirection() {
      try {
        const { data } = await axios({
          method: 'GET',
          // 카카오는 쿼리스트링으로 보내야한다.
          // 서버에서 처리하는 endpoint를 반드시 잡아놔야할 것
          url: `${baseURL}/kakao/callback?code=${code}`, 
        });
        
        saveToken(data.token);
        dispatch(loginAction({ email: data.email, nickname: data.nickname }));
        history.replace('/');
      } catch (error) {
        console.error(error);
      }
    }
    
    handleKaKaoLoginRedirection();
  }, []);
profile
오네명입니다.

0개의 댓글