Re:wha 프로젝트, 사용 기술과 오류 해결

Maru·2022년 8월 12일
0
post-thumbnail

구현 하면서 어려웠던 것, 기억해야할 점 등을 위주로 작성하고자 한다.

원래 내가 맡았던 페이지는
1. 메인페이지
2. 카테고리 페이지
3. 검색 페이지
이 세가지였다.

하지만 작업하다보니 이 세가지 페이지 외에도 이것저것 많이 작업했다.

1. 유저 인증 관련

  • 우리는 서버로부터 발급받은 token을 localstorage에 저장해 관리했다. 페이지가 새로고침되어도 로그인이 유지되어야했기 때문이다.
    (localstorage에 token을 저장하는 것은 보안 이슈가 있긴 했지만, 구현과 사용이 간단하다는 장점 때문에 채택했다. 우리 동아리는 입문자 동아리라 localstorage가 최선의 선택이기도 했다.)
  • 유저 정보를 Redux-persist로 localStorage에 저장했다.
  • 사이트의 거의 모든 페이지에서 유저 정보가 필요하다. 예를 들면 유저의 id, role, 닉네임 등이 있다. 매번 프로필 조회 api로 프로필을 조회하지 않기 위해 그냥 유저 정보를 로컬에 저장해서 사용했다.
  • 프로필 조회에 실패하면 에러가 심하게 나기 때문에 최대한 api 요청 횟수 자체를 줄이고자 하는 목적 때문이었다.
  • (보안을 포기하고 안정성을 높이고자 했다...)

이슈 발생

문제 : token을 로컬스토리지에 저장 한 후, 바로 꺼내쓸 수 없다. vaule의 값이 업데이트 됐을 때, 페이지를 새로고침 하지 않으면 바뀐 값이 반영되지 않는 이슈가 있었다.

  • 해결 : 응답으로 발급 받은 토큰을 로컬스토리지에 저장은 하지만, 프로필 조회를 할 때는 response로 받은 값을 바로 사용했다.

axios 인스턴스 세팅

  • http 컴포넌트 : token이 있을 때만 헤더에 token을 추가해준다.
import axios from "axios";

export const http = axios.create({
  baseURL: "https://api.rewha2022.com",
});

http.defaults.withCredentials = true;

// localstorage에서 토큰을 조회
const token = JSON.parse(localStorage.getItem("token")) ?? false;

// 토큰이 있는 경우에만 헤더에 토큰을 넣는다.
http.defaults.headers.common["Authorization"] = token
  ? `Bearer ${token}`
  : null;

회원가입 > 로그인 > 프로필 조회 플로우

그리하여 완성된 최종 코드는 다음과 같다.


    // 회원가입
      PostUser(id, password, name)
      .then(data => {
        
        // 로그인
        GetUser(id,password)
        .then(data=>{
          const token = data.data.access_token; 
          // 로컬에 유저 토큰 저장
          window.localStorage.setItem("token", JSON.stringify(token)); 
          
          // 프로필 조회
          GetProfile(token)
          .then(res=>{
            dispatch(setUser(res.data));
            navigate("/") //메인페이지로 이동
          })
          
          // ...catch는 생략
     

의문점 : 동기적인 처리를 위해 .then() 안에 비동기 함수를 중첩하여 사용해도 문제가 없는가?
의도한대로 작동하기는 하지만, 코드 가독성이 매우 안 좋아진다. 좀 더 좋은 방법이 없을까 고민했으나 마감 때문에 다른 시도를 하지 못해 아쉬웠다.




2. 댓글 스크롤 로직

문제 : 새 댓글 작성 후, 페이지 맨 아래로 내려가야함.
그 이외의 액션에선 스크롤이 움직이면 안된다.

  • 단순하게 댓글 작성 함수 실행 후 scroll 함수를 넣는다고 해결되지 않는다. api 통해서 response를 받아오는데 시간이 걸리기 때문에, 이를 동기적으로 처리하는게 굉장히 애매했다.
  • 댓글을 새로 작성했는지에 대한 여부를 구별할 useState 선언
  • 댓글을 새로 작성했을 때 true로 변경
    * (댓글에 변화가 있고) && (댓글을 새로 작성했음) 에서만 scroll을 맨 밑으로 내린다. 이를 useEffect로 구현했다.
// thisComments는 get을 통해 받아온 comments 목록이다.
const [isAdd, setIsAdd] = useState(false);

// 댓글을 get 해왔는데, 댓글 목록에 변화가 있다면 useEffect 실행
  useEffect(() => {
    // 방금 새로 댓글을 추가했다면 스크롤을 내린다. 
    if (isAdd == true) {
      scrollToBottom();
      setIsAdd(false);
    } else {
      setIsAdd(false);
    }
  }, [thisComments]);



3. 페이지네이션

페이지 당 게시물 수(limit), 현재 페이지 번호(page)
첫 게시물의 위치(offset)을 계산합니다.

// 공지 렌더링 하는 부분

const [notices, setNotices] = useState(noticeData);
  const [limit, setLimit] = useState(5);
  const [page, setPage] = useState(1);
  const offset = (page - 1) * limit;

 {notices.slice(offset, offset + limit).map(notice => {
        return (
          <>
            공지 렌더링 부분
          </>
        );
      })}

페이지네이션 컴포넌트

function Pagination({ total, limit, page, setPage }) {
  
  // Math.ceil() : 올림 함수 
  const numPages = Math.ceil(total / limit);

  return (
    <>
      <Nav>
        <Button onClick={() => setPage(page - 1)} disabled={page === 1}>
          <img src={left} />
        </Button>

        {Array(numPages)
          .fill()
          .map((_, i) => (
          
            <Button
              key={i + 1}
              onClick={() => setPage(i + 1)}
              aria-current={page === i + 1 ? "page" : null}
            >
              <p>{i + 1}</p>
            </Button>

          ))}
          
        <Button onClick={() => setPage(page + 1)} disabled={page === numPages}>
          <img src={right} />
        </Button>

      </Nav>
    </>
  );
}

...

const Button = styled.button`
  ...

  &[disabled] {
    cursor: revert;
    transform: revert;
  }
  &[aria-current] {
    cursor: revert;
    transform: revert;
    p {
      color: var(--black);
    }
  }
`;

4. 최적화 관련

작업 중 폰트 로딩이 매우 느린 현상이 발생했었다.


5. 배포 오류

Error: Command "npm run build" exited with 127

  • 파일 이름 오류
  • package.json에 잘못된 이름의 dependency가 있었다.

6. 구글 애널리틱스 사용

시행착오

.env 파일을 깃헙에 올리면 안된다는 것을 알고 있었다. 그럼 비밀키는 어디에 저장하는가? 바로 깃허브에 저장하면 된다. 이번 기회에 처음 사용해봤다.

profile
함께 일하고 싶은 개발자

0개의 댓글