Project Slow-postbox #6 버그 해결 및 게스트 로그인 기능 구현(서버)

시작하며

지난 주에 코드 스테이츠의 마지막 과정인 Final Project를 마치고 수료를 하였다. 2주 동안 First Project인 느린 우체통을 마치고 바로 프로젝트를 진행했기 때문에 마지막에 발견한 버그들이나 보완점들을 해결하지 못하였다. Final Project를 시작하기 전에 추후 해결해야할 사항들을 정리해놓았고 이제 모든 과정이 끝났기 때문에 팀원들과 버그를 해결하는 시간하고 기능을 추가하는 시간을 가지게 되었다.

auth 인증 추가

로그인/관리자 여부에 따라서 네비게이션 바의 버튼이 바뀌기 때문에 UI를 통해서는 로그인이 필수인 페이지 또는 관리자만 접근할 수 있는 페이지에는 접속이 어렵지만 url를 직접 입력한다면 접근이 가능하다. 그래서 페이지 접근시 auth 인증을 통해 로그인한 유저/관리자가 아닌 경우 모달창을 띄우는 방식으로 접근을 불가능하도록 설정해주었다.

관리자 페이지

function AdminPage() {
  ...
    const [ modal, setModal ] = useState(false)
  
    const isAuthenticated = () => {
      axios
        .get(`${process.env.REACT_APP_SERVER_API}/user/auth`, {
          withCredentials: true,
        })
        .then((res) => {
          if (!res.data.data.admin) {
            setModal(true)
          }
        })
        .catch((err) => {
          setModal(true)
        });
    };

    useEffect(()=>{
      isAuthenticated();
    },[])
  
    return (
	...
        {modal && <ModalAdmin />}
	...
  );
}

관리자 페이지 접근시 쿠키의 accessToken을 서버로 보내 관리자 여부를 받아서 로그인하지 않았거나 관리자인 경우, 모달 창을 띄우는 형태로 코드를 작성하였고, 확인 버튼 클릭시 홈으로 페이지가 이동되도록 설정하였다.

받은 편지함, 보낸 편지함, 마이페이지

  ...
  const isAuthenticated = () => {
		axios
		  .get(`${process.env.REACT_APP_SERVER_API}/user/auth`, {
			withCredentials: true,
		  })
		  .catch((err) => {
			setModalLogin(true)
		  });
	  };

   useEffect(()=>{
      isAuthenticated();
   },[])
  ...
  return (
	...
        {modalLogin && <ModalLogin/>}
	...
  );

로그인이 필요한 페이지도 마찬가지로 auth 인증을 통해 모달창을 띄우는 형태로 작성해주었다. 리덕스로 로그인과 관련된 정보가 관리되고 있지만 리덕스의 데이터로 검사를 하면 유저 정보를 받아오기 전에 로그인/관리자 여부를 확인하는 함수가 먼저 실행되기 때문에 위 코드 처럼 .then을 사용하여 서버에서 데이터를 받아온 후 확인해 주는 과정으로 구현하였다.

게스트 로그인 기능 서버 구현

느린 우체통은 로그인 없이 이용할 수 있는 기능이 없기 때문에 게스트 로그인 기능을 추가하기로 하였다. 데이터 베이스 스키마를 짤때 게스트 로그인 기능과 회원탈퇴로 인한 편지 삭제를 방지하기 위해서 외래키를 별도로 설정해주지 않아서 큰 수정없이 기능 구현이 가능했다.

게스트 로그인 시 임의의 값으로 유저 정보가 생성되는데 게스트의 경우 편지쓰기/보낸 편지함 페이지만 이용 가능하고 게스트가 이용할 수 없는 페이지는 로그인 이후 이용이 가능하다고 안내 문구를 띄워주는 방식으로 구현하기로 결정하였다.

클라이언트에서 게스트 여부에 따라서 페이지를 다르게 보여지게 하기 위해서 users 테이블에 isGuest 컬럼을 추가하였고, 이후 게스트 로그인/로그아웃 요청시 데이터가 생성/삭제 되도록 서버 구현을 해주었따.

//게스트 로그인 요청시
const db = require('../../db');
const jwt = require("jsonwebtoken");

module.exports = async (req, res) => {
  try {
    let duplication = true;
    let randomSet = '';
    while(duplication) {
        randomSet = Math.random().toString(36).slice(2,7);
        const check = await db.query('SELECT id FROM users WHERE email=?', [`게스트${randomSet}`]);
        duplication = check[0].length
    }
    const sql =
      'INSERT INTO users (name, email, oauth, admin, isGuest) VALUES(?,?,?,?,?)';
    const params = ['게스트', `게스트${randomSet}`, 0, 0, 1];
    const guestData = await db.query(sql, params);
    const [result, fields, err] = await db.query('SELECT * FROM users WHERE email=?', [`게스트${randomSet}`]);
    if (err) throw err;
    else {
        console.log(result)
        const { id, name, email, oauth, admin, isGuest } =
          result[0];
        const payload = {
          id,
          name,
          email,
          oauth,
          admin,
          isGuest
        };
        const accessToken = jwt.sign(payload, process.env.ACCESS_SECRET, {
            expiresIn: "1d",
          });
          res
            .cookie("accessToken", accessToken, {
              maxAge: 24 * 6 * 60 * 10000
            })
            .status(200)
            .json({
              data: { accessToken: accessToken, payload },
              message: "로그인되었습니다",
            });
    }
  } catch (err) {
    return res.status(404).json({ data: null, message: '서버 에러' });
  }
};

게스트 로그인시 Math.random()을 사용하여 임의의 문자열을 생성하고 기존에 있는 데이터인지 확인하여 게스트 유저 정보를 확인하는 과정을 거치게 된다. 이후 기존처럼 jwt를 사용하여 accessToken 발급하여 클라이언트에 전달해주도록 설정하였다.

//로그아웃 시
const db = require("../../db");
const jwt = require("jsonwebtoken");

module.exports = async (req, res) => {
  const accessToken = req.cookies.accessToken;
  const verified = jwt.verify(
    accessToken,
    process.env.ACCESS_SECRET,
    (err, decoded) => {
      if (err) return null;
      return decoded;
    }
  );

  if (verified.isGuest) {
    await db.query('DELETE FROM users WHERE id=?', [verified.id]);
  }
  res.clearCookie("accessToken", { path: "/" });
  res.status(205).json({ message: "로그아웃되었습니다" });
};

그리고 로그아웃시 accessToken만 삭제하는 부분 외에 추가적으로 게스트인지를 확인해서 데이터베이스에서 삭제하는 형태로 서버 구현을 마쳤다.

유저 테이블에 게스트 정보를 생성하지 않고 편지만 보낼 수 있도록 클라이언트에서 구현을 할 수도 있지만 보낸 편지함 기능을 사용하려면 실제 유저정보가 있어야 하기 때문에 위와 같은 방식으로 구현하였다.

마치며

마무리했던 First Project를 다시 수정하는 과정이 새로운 프로젝트를 진행하는 것만큼 즐겁지는 않지만 중요한 과정이라고 생각한다. 이제 시작하는 초보 개발자의 입장에서 아무런 버그없이 의도대로 동작하는 완벽한 서비스를 한번에 완성한다는 것은 거의 불가능하다. 예상치 못한 버그를 해결하고 유지보수하는 경험은 다른 프로젝트에서 보다 좋은 코드를 작성하기 위한 밑거름이 된다고 생각하면서 진행한 것 같다.

좋은 웹페이지 즐겨찾기