캡스톤 디자인 프로젝트로 학우들이 기존 수기로 체크하던 졸업사정 셀프 테스트를 자동화시키는 프로젝트를 진행했다.
총 4명(프론트2, 백2)으로 팀을 구성했으며, 나는 프론트 개발에서 화면UI 구성, 백엔드와 API 연결 등을 맡았다.
처음으로 배포까지 진행했던 프로젝트였기 때문에, 개발 중 많은 이슈들이 발생했다.
화면을 구성한 뒤, 백엔드에서 데이터를 받아오기 위해 연결을 했는데 받아와지지 않는 에러가 발생했었다. 프론트/백 코드에서는 문제가 없었는데 이런 에러가 발생한 것이 처음에는 잘 이해가 되지 않았다.
에러를 확인해보니 CORS에러가 발생했다고 하여 이 문제가 무엇인지 찾아보았다.
link
태그의 href 에서 다른 사이트의 css 등 리소스에 접근하는 것이 가능하다. img
태그의 src 에서 다른 사이트의 이미지 등의 리소스에 접근하는 것이 가능하다. 내가 CORS에러가 발생했던 이유는 백엔드와 포트가 달라서 발생했던 경우였다.
해결하는 방법으로는 첫 번째, 서버에서 해결하는 방법이 있고 두 번째, 프론트에서 해결하는 방법이 있다. 처음에는 서버에서 해결하는 방법을 찾지 못하여 프론트 단에서 해결하도록 방법을 찾았고 proxy를 설정하여 문제를 해결했다.
서버에서 요청을 하게될 때에는 브라우저의 규약인 CORS 정책에 영향을 받지 않는다고 한다. 그러한 이점을 이용하여 proxy server라는 출처를 통하기 때문에 CORS 정책을 위반하지 않고 우회할 수 있었다.
react 16.8버전에 새로 추가되었으며, hooks를 사용하면 class를 작성할 필요없이 상태 값과 여러 리액트 기능을 사용할 수 있다.
프로젝트를 하면서 자주 사용해보지 않았던 hooks였다.. 하지만 요즘에는 훅스를 이용하여 많이 프로그램을 만든다고 하여 공부하여 프로젝트에 적용해보기로 했다.
훅스를 사용하기 위해서는 생명주기부터 이해를 해야한다.(추후 포스팅하여 설명할 예정)
처음 사용해보는 부분이라 직접 부딪쳐보고 수정해야 되는 부분이 대부분이였다.
그 중에서 페이지네이션을 구현하는 부분이 어려웠다🥲
처음에는 프론트단에서 백엔드의 글 목록 전체를 받아와 10개씩 잘라서 구현하려했지만, 그렇게 되면 페이지가 옮겨질 때마다 항상 백엔드에서 전체 글 목록을 받아와야 하기 때문에 로딩 시간이 오래 걸린다는 단점을 발견했다. 백엔드에서 10개씩잘라서 받아와지도록 구현하여 해결했다.
백엔드 단에서 시작 페이지와 끝페이지 정보를 받아와 해당 페이지를 돌면서 array.push하는 방식으로 처리했다.
const paging = () => {
let array = [];
for (let i = searchHelper.startPage; i <= searchHelper.endPage; i++) {
array.push(
<div
className="Board__page--button"
onClick={(e) => handlePageClick(i, e)}
>
{i}
</div>,
);
}
return array;
}
/*
페이지 번호를 클릭해서 넘어가는 정보는 다음과 같이 처리했다
*/
const [searchHelper, setSearchHelper] = useState([]);
const handleNextBtn = async (e) => {
e.preventDefault();
const response = await axios.get(
`${API_URL}${PORT_NUMBER}/Board/?page=${searchHelper.nextBlock}`,
);
setSearchHelper(response.data.searchHelper);
setInputData(response.data.boardDtoList);
};
const handlePrevBtn = async (e) => {
e.preventDefault();
const response = await axios.get(
`${API_URL}${PORT_NUMBER}/Board/?page=${searchHelper.prevBlock}`,
);
setSearchHelper(response.data.searchHelper);
setInputData(response.data.boardDtoList);
};
const handlePageClick = async (i) => {
const response = await axios.get(
`${API_URL}${PORT_NUMBER}/Board/?page=${i}`,
);
setInputData(response.data.boardDtoList);
};
또 구현을 하면서 힘들게했던 부분은 글 수정 기능이었다.
글을 수정하기 위해 들어가면 기존 정보를 보여주고 수정하지 않으면 그대로의 정보를 다시 보내줬어야하는데, 빈값으로 넘어가져서 똑같이 쓰지 않으면 내용이 사라지는 에러가 있었다.
이를 해결하기 위해서 다음과 같이 코드를 작성했다.
/**
* @description submit 이벤트
*/
const handleSubmit = async (e) => {
e.preventDefault();
if (password !== inputData.brdPassword) {
alert('비밀번호가 틀립니다. 다시 입력해주세요');
}
if (password === inputData.brdPassword) {
if (title === '' && writer === '' && content === '') {
await axios.put(`${API_URL}${PORT_NUMBER}/Board/${params}`, {
brdKey: params,
brdTitle: EditBoard.brdTitle,
brdWriter: EditBoard.brdWriter,
brdContent: EditBoard.brdContent,
brdPassword: password,
});
alert('수정완료 되었습니다.');
navigate('/Board');
} else if (title === '' && writer === '') {
await axios.put(`${API_URL}${PORT_NUMBER}/Board/${params}`, {
brdKey: params,
brdTitle: EditBoard.brdTitle,
brdWriter: EditBoard.brdWriter,
brdContent: content,
brdPassword: password,
});
alert('수정완료 되었습니다.');
navigate('/Board');
} else if (content === '' && writer === '') {
await axios.put(`${API_URL}${PORT_NUMBER}/Board/${params}`, {
brdKey: params,
brdTitle: title,
brdWriter: EditBoard.brdWriter,
brdContent: EditBoard.brdContent,
brdPassword: password,
});
alert('수정완료 되었습니다.');
navigate('/Board');
} else if (content === '' && title === '') {
await axios.put(`${API_URL}${PORT_NUMBER}/Board/${params}`, {
brdKey: params,
brdTitle: EditBoard.brdTitle,
brdWriter: writer,
brdContent: EditBoard.brdContent,
brdPassword: password,
});
alert('수정완료 되었습니다.');
navigate('/Board');
} else if (content === '') {
await axios.put(`${API_URL}${PORT_NUMBER}/Board/${params}`, {
brdKey: params,
brdTitle: title,
brdWriter: writer,
brdContent: EditBoard.brdContent,
brdPassword: password,
});
alert('수정완료 되었습니다.');
navigate('/Board');
} else if (title === '') {
await axios.put(`${API_URL}${PORT_NUMBER}/Board/${params}`, {
brdKey: params,
brdTitle: EditBoard.brdTitle,
brdWriter: writer,
brdContent: content,
brdPassword: password,
});
alert('수정완료 되었습니다.');
navigate('/Board');
} else if (writer === '') {
await axios.put(`${API_URL}${PORT_NUMBER}/Board/${params}`, {
brdKey: params,
brdTitle: title,
brdWriter: EditBoard.brdWriter,
brdContent: content,
brdPassword: password,
});
alert('수정완료 되었습니다.');
navigate('/Board');
} else {
await axios.put(`${API_URL}${PORT_NUMBER}/Board/${params}`, {
brdKey: params,
brdTitle: title,
brdWriter: writer,
brdContent: content,
brdPassword: password,
});
alert('수정완료 되었습니다.');
navigate('/Board');
}
}
return;
};
팀원들이 모두 열심히 해줘서 교내 창의적 종합설계에서 대상을 받을 수 있었다. 현재 기준으로 AWS를 통해 배포 서비스를 유지하고 있으며, 여기에서 확인 가능하다.
깃으로 형상관리를 처음하면서 브랜치관리, 커밋 컨벤션, 파일 관리 등을 하나도 정하지 못했는데, 나중에 내가 뭘 수정했는지 찾아볼 때 조금 시간이 걸렸던 기억이 있다. 다음에는 개인이든 팀 프로젝트든 규칙을 정하고 진행하면 추후에 나 혹은 다른 팀원이 수정한 부분을 찾을 때 좀 더 용이할 것 같다라고 생각한다. 그리고 aws배포에 대해 지식이 없어서 학교 선배들에게 많은 도움을 받았는데, 배포에 대해서도 공부할 필요가 있다고 느꼈다.
처음 화면을 구성하고, 기능을 구현해야할 때는 이걸 내가 할 수 있을까?라는 생각이 컸는데, 막상
직접 화면을 구성하면서 다양한 css 문법을 사용해보는 경험과 다양한 hooks를 사용하며 프로젝트를 마무리하면서 어렵게 느낀 부분을 해결할 때마다 재미를 많이 느끼고 자신감이 많이 생겼다.