원티드 프리온보딩 인턴십 교육 (11차) 사전과제 주제는 투두리스트 만들기였습니다. 간단한 투두리스트 라고 생각할 수 있지만, 뭐든지 간단한만큼 신경쓸게 더 많다는 사실.. 방심하지 않고 핵심 기능들을 최대한 깔끔하게 구현해보자 라는 생각으로 임해보았습니다.
여담이지만 .. 왜 저는 항상 팀장이 되는걸까요 ..? 흑..
사전 개인과제 요구사항
기능구현에 직접적으로 연관된 라이브러리 사용은 허용되지 않습니다.(React-Query 등)
README.md 작성은 필수입니다. 아래의 사항은 반드시 포함되도록 해주세요
페이지별로 요구되는 경로는 도메인 URL뒤에 바로 이어지도록 설정해주세요
기본적으로 진입 루트가 회원가입 -> 로그인 -> 투두리스트 였고, 기능 구현에 연관된 라이브러리는 사용하지 않는것이 기본 규칙이었습니다.
1주차 팀 과제 요구사항
팀빌딩이 이뤄지고 진행되는 1주차 팀 과제는BestPractice를 선정하여 사전과제를 리팩토링 하는것이었습니다.
Best Practice란 모범사례라는 말로서, 특정 문제를 효과적으로 해결하기 위한 가장 성공적인 해결책 또는 방법론을 의미합니다.
쉽게말해사전과제의 핵심 기능을 파트별로 나눠, 가장 잘 작성된 코드를 선정해 선정된것들로만 이뤄진 프로젝트를 완성해 제출하는 것이 1주차 과제였습니다.
1주차 세션에서 진행했던 협업을 위한 툴들을 적용시키기 위해ES Lint 와 Prettier, 그리고 husky를 사용하여 포멧팅을 자동화 하도록 진행했고,
너무 세분화하여 진행하지 않고 큰 부분들만 나눠서 불필요한 딜레이를 줄이는것을 목표로 진행되었기 때문에고민사항은 크게 3가지로 분류하여 BestPractice 를 선정했습니다. (진행은 팀별 디스코드 채널,노션을 생성해 회의와 회의내용을 정리하여 기록하며 진행했습니다.)
파일트리
전체적으로 팀원들이프로젝트의 디테일한 폴더 구조에 대한 고민이 많았기 때문에여러부분을 참고하여 진행했지만실무를 겪어보지 못했기 때문에 프로젝트의 규모별로 괜찮은 파일트리를 선정하기 어려웠다는 의견들이 많았습니다.
인증/인가 로직
인증에 대한 로직을어디서구현해야 할지, 공통 로직을분리할 수 있을지, 그리고 그것을어떻게구현해야 할지에 대한 고민이 많았습니다.
컴포넌트 분리
컴포넌트를 어떻게어떠한 기준으로 분리하여 유지보수에 최적화된 컴포넌트를 구현할 수 있을지에 대한 고민이 공통적으로 존재했습니다.
과제 진행중 고민되었던 부분
개인 : 파일트리
개인으로 진행했던 사전과제의 경우에는 파일트리를 프로젝트 전체 깊이가 깊어지더라도 세분화하여 폴더를 생성하여 각 폴더별로 메인이 될 수 있는 컴포넌트를 index.tsx 로 생성하여 관리하는 구조를 택했습니다.
해당 구조가 개인적으로 각각의 컴포넌트에 대한 작명만 잘한다면 도서관의 원하는 도서 찾기처럼 크게 불편함이 없는 상태로 세분화를 할 수 있겠다는 생각이 들었습니다.
이전에 진행했던 개인 프로젝트는 아무래도 하나하나를 세분화 하다보니 전체적으로 깊이가 너무 깊어져서 오히려 가독성을 더욱 해칠 수 있겠다는 생각이 들었기 때문에이번 프로젝트는 진행하면서 자식 컴포넌트 (HeaderMenu 의 Menu 같은)는 따로 폴더를 생성하지 않고 해당 컴포넌트의 이름으로 네이밍을 하는 방식으로 진행하였습니다.
팀 : 파일트리
팀과제로 진행된 프로젝트의 경우에는 일단 컴포넌트 구현에 앞서서 정확한 파일트리와 인증로직 구현을 진행 후에 컴포넌트를 구현하는게 맞다고 생각했기 때문에 파일트리를 먼저 정하게 되었습니다.
전체적으로 저는 백엔드를 학습하다가 프론트엔드를 시작했었던지라, 백엔드 프로젝트를 진행할 땐 항상 확장에 대한 가능성을 열어둔 채로 프로젝트를 진행했기 때문에 최대한 쪼갤 수 있는 부분은 쪼개고 각각의 모듈에 대해서 관심사를 분리하는것을 메인으로 삼고 진행했으나,
공통적으로 프론트엔드 개발자분들은 대부분 현재 진행하는 프로젝트의 규모에 따라 방식을 정하는듯 하셨습니다.
저희 팀의 경우 현재 진행해야 하는 투두리스트 또한 규모 자체가 크지 않았기 때문에과한 세분화는 지양하고 최대한 커뮤니케이션에 문제가 없게끔 파일트리를 정의하여 깔끔한 프로젝트를 진행하기로 결정되었습니다.
여기에 api로직에 대한 부분들은 저의 방식을 BestPractice 로 선정해 apis 폴더에 각 컴포넌트 별로 비즈니스로직을 분리하여 선언하도록 구현했습니다.
해당 부분에 있어서는기본적으로 큰 부분 (컴포넌트들,api들, 페이지들 등) 으로 나누어 폴더를 생성 후 각 부분에 맞춰서 필요시에 폴더를 생성하고, 큰 이유가 없다면 파일만 생성하여 관리하는 구조를 택했고,전체적으로 파일 트리는 아래와 같이 정의되었습니다.
팀 진행으로 인해 얻어간것들 (파일트리)
어떻게 보면 제가 개인으로 정의했던 파일트리는 예를 들었을 때 쓸데없이 즉섭밥을 데워먹어야하는 상황임에도 직접 지은 밥을 고수하는 듯한 느낌을 줄 수도 있겠구나 .. 라는 생각을 하게 되었습니다.
다른분들의 의견을 통해 상황별로 해당 구조가 필요한 경우가 있겠지만 너무 과하게 멀리보고 진행하기보다는 확실한 규모를 정하고 프로젝트를 진행하는게 좋겠다는 생각을 다시 한번 하게 되었습니다.개발은 아무래도 나 혼자 하는것이 아니기 때문에 ..
개인 : 인증 로직 구현
인증 로직의 경우에는 기본적으로요청시에 헤더에 토큰을 포함시키고, 토큰이 만료되거나 비밀번호가 옳지 않을때는 공통적으로 401에러를 반환하기 때문에axios interceptor 로 분리하여 로직을 구현할 수 있겠다는 생각이 들었습니다.
이에 대해서 들었던 생각은 두가지였습니다.
ContextAPI 를 사용하여 인증 상태값 저장하기
단순히 LocalStorage 에서 토큰값을 가져와서 헤더에 포함시키기
이번 과제의 경우에는메인 기능을 구현하는데에 있어서 관련된 라이브러리를 설치하여 사용하는것이 금지되었기 때문에ContextAPI 를 사용하여 구현하는것을 생각했습니다.
하지만 Axios Interceptor 의 경우에는 해당 로직 내부에서 hook사용이 불가능했기 때문에 구현에 있어서 문제가 발생했습니다.
따라서Axios Interceptor 에서는 매 요청시마다 로컬스토리지에 저장된 값을 확인이 가능했기 때문에 로컬스토리지에서 토큰을 찾아서 같이 포함시켰고, Context API는 인증 여부에 따른 라우팅을 담당하는데에 사용하였습니다.
단순히 라우팅시에도 localStorage 에서 값을 찾아오면, 해당 변수에 저장된 boolean 값이 계속 업데이트 되는것이 아니기때문에 제대로된 구현이 어려웠습니다. +@ 로 useEffect를 사용하여 navigate시에는 블링크 현상이 발생했기 때문에 신경쓸 부분이 많았습니다. (예: 토큰이 없는 상황에서 Todo로 라우팅시에 SignIn 컴포넌트로 네비게이트)
하나의 기능에 대한 구현을 여러 방법으로 구현하는것 처럼 보일 수 있었지만, 이렇게 지속적으로 인증 상태가 변경되는 부분을 감시해야하는 상황에서는 Context API 를 사용했고, Interceptor 에서는 LocalStorage 의 토큰 유무를 매 요청마다 확인하여 반영할 수 있었기 때문에 단순히 getItem 으로 유무를 확인 후 포함하도록 구현했습니다.
투두리스트를 구현하면서 가장 고민되었던 컴포넌트 분리 부분은 아무래도 요구사항 중투두를 수정할 때 기존의 text가 사라지면서 input 으로 변경 된 후 '수정', '삭제' 버튼 또한 '제출','취소' 버튼으로 변경이 되도록 구현하는 것이었는데,
해당 부분을 수정모드일때의 컴포넌트를 구현하여 반영을 할지, 혹은 하나의 컴포넌트에 반영을 할지 고민이 많이 되었고, 주변의 어느분께 헬프를 요청하여 아이디어를 얻게 되었습니다.
아무래도 현업에서 개발을 하고 계시는 프론트엔드 개발자 분들도 공통적으로 고민하는 부분이 컴포넌트 분리에 대한 고민이라고 하시면서, 본인은 각 컴포넌트를 역할별로 분리한다고 하셨고,해당 투두에대한 수정과 삭제는 해당 투두(항목) 밖에선 일어날 일이 없으므로 굳이 분리할 필요가 없다고 하셨고, 해당 의견을 참고하여 구현하였습니다.
기본적으로 큰 페이지는 투두 입력창 / .map() 을 활용한 투두 로 구성이 되도록 하였으며 투두컨텐츠라는 컴포넌트에 수정/삭제 로직을 포함시켜 구현하였습니다.
TodoInput 또한 마찬가지로 useFormControl 훅을 재사용해 핵심 로직을 분리시켜서 가독성에 신경썼습니다.
TodoContent 는 과제 명세에 포함된 내용을 참고하여디테일한 부분을 살리려 노력했습니다.(수정중에 체크박스를 클릭해도 업데이트가 진행된다던지 하는 부분들)
저같은 경우에는 팀원 중 기본적으로 구현이 되어야하는 컴포넌트 (예 : input, button 등) 을 먼저 구현 후 각 큰 컴포넌트 별로 다시 스타일링 및 구현을 진행한 후 페이지를 완성한 팀원분의 코드를 보고 구조적으로 깔끔하다 생각되어 해당 팀원분을 선정하였습니다.
스타일링 또한 기본적으로 코딩 컨벤션을 지켜가면서 기능 / 스타일링을 철저히 분리해 가독성을 살리셨고, 다른 팀원분들 또한 마찬가지로 의견이 거의 동일하게 해당 팀원분을 선정하여 진행됐습니다.
여기서 제가 진행했던 부분은 TodoInput 을 구현하는 것이었는데, 구현하는김에 제가 개인적으로 구현했던 useFormControl 훅을 재사용해 useInput 이라는 커스텀 훅을 구현했고,
해당 훅을 구현시에 고민했던 부분 (불필요한 리턴값에 대한 핸들링) 을 반영하여배열 타입으로 반환하지 않고 객체 타입으로 반환하도록 구현했습니다.
여기에 BestPractice 로 선정된 팀원분의 의견 (현업에선 UI/UX디자이너 분들께서 MUI , Chakra같은 스타일링 라이브러리를 사용하는것을 안좋아한다)을 참고하여 직접 common 컴포넌트를 구성해 구현하도록 했기때문에, Input 컴포넌트를 직접 구현하게 되었습니다.
interface IUseInputReturn<T> {
onChange: React.ChangeEventHandler<HTMLInputElement>;
value: T;
setValue: React.Dispatch<React.SetStateAction<T>>;
isValidated: boolean;
setIsValidated: React.Dispatch<React.SetStateAction<boolean>>;
setFocus: () =>void;
setBlur: () =>void;
}
const useInput = <T>(options: {
regex?: RegExp;
ref?: RefObject<HTMLInputElement>;
initialValue?: T;
}): IUseInputReturn<T> => {
const { regex } = options || {};
const [value, setValue] = useState<T>((options.initialValue as T) ?? (''as unknown as T));
const [isValidated, setIsValidated] = useState<boolean>(false);
const validateValue = (value: T) => {
if (typeof value === 'string' && regex) {
const isValid = regex.test(value);
setIsValidated(isValid);
} else {
setIsValidated(false);
}
};
const setFocus = () => {
if (options.ref) {
options.ref.current?.focus();
}
};
const setBlur = () => {
if (options.ref) {
options.ref.current?.blur();
}
};
const onChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
const newValue = e.target.value as unknown as T;
if (newValue !== value) {
setValue(newValue);
validateValue(newValue);
}
};
// 기본적으로 훅 사용할때 <type> 형식으로 타입 지정 해주시거나 (객체도 가능) 초기화값 지정해주시면 타입 자동으로 들어갈겁니다./* 폼 제출시 유효성 검사 초기화, 값 초기화를 위해 setter 까지 반환하도록 했습니다.
전체적으로 빈 값이 들어가는 경우는 없기때문에 정규표현식으로 관리하도록 구현했습니다.*//* const { data: data1, isLoading: isLoading1 } = useCustomHook(params1);
const { data: data2, isLoading: isLoading2 } = useCustomHook(params2);*//* 여러번 선언해야할 경우 위와 같이 사용하면 됩니다. (여러번 사용하지 않더라도 변수명 헷갈리지 않게 하기 위해 이렇게 사용하시는걸 추천드립니다.) */return { onChange, value, setValue, isValidated, setIsValidated, setFocus, setBlur };
};
exportdefault useInput;
개인과제로 진행했던 hook은 아무 생각 없이 단순 string 만을 핸들링하도록 구현했고, 이를 반성하며 리팩토링을 통해 제네릭 타입으로 checkbox 등에도 사용할 수 있도록 변경하였습니다. 여기에 refObject 를 전달받아서 focus나 blur 같은 이벤트도 구현할 수 있도록 전체적으로 react-hook-form 의 기능을 모방하여 사용할 수 있도록 구현하였습니다.
input 의 경우에는 기본적으로 input 태그가 갖고있는 attribute를 다 받아올 수 있도록 인터페이스 선언 시에 HTMLProps를 상속받아 사용하도록 구현했고, ref 같은 경우에는 따로 전달하는 값이 있기때문에 Omit 을 사용하여 해당 인터페이스중 ref 항목을 제외하고 상속받도록 구현했습니다.
팀 진행으로 인해 얻어간 것들 (컴포넌트 분리)
Omit과 HTMLProps 라는 새로운 것을 알게되었습니다.
common component 를 구현할 때 쓸데없이 interface 에 property를 와바박 쓸 일이 없어졌습니다..
아무래도 컴포넌트 구현을 저 혼자 하는게 아니었기 때문에 협업을 통해 리팩토링에 신경쓰게 되었고, useInput 을 구현할 때 제네릭 타입을 처음 사용하는 등의 경험이 소중하게 느껴졌습니다.
마찬가지로 주석처리에 신경쓰게 되었고, 커뮤니케이션의 대부분이 컴포넌트 구현 파트에서 이뤄졌기 때문에 실무를 정말 간접적으로 경험할 수 있었습니다.
오히려 좋았던점은 커스텀훅을 사용하는 법이 익숙치 않았던 팀원분께서 계셨고, 이로 인해서 주석처리나 가독성 좋은 코드를 작성하려 많이 노력했던 부분이 오히려 실력상승으로 이어지게 되었습니다.
개인 : 테스팅
이번 과제에는 존재하지 않았지만, 개인적으로 Jest를 이용한 테스트코드 작성에 대한 흐름이 매번 궁금했었고,전체적으로 프로젝트 규모가 크지 않으니 이번 기회에 배워서 작성해볼 수 있겠다 !라는 생각을 갖고 테스트코드 작성을 시도해보았습니다.
테스트는 기본적으로 각 Page별로 진행했고, 상황별로 mocking 을 통해 api를 호출했을때의 상황, routing 유무 등을 테스트하였습니다.
Test Suites: 3 passed, 3 total
Tests: 28 passed, 28 total
Snapshots: 3 passed, 3 total
Time: 4.639 s
Ranall test suites related to changed files.
이런식으로 총 28개의 단위테스트를 3개의 페이지별로 진행했고, 모두 통과하였습니다. 다행히 스프링부트로 개발을 하면서 단위테스트를 진행했던 경험이 있어서 테스팅 라이브러리를 익히는데에 큰 문제는 없었으나
어려웠던건 스프링부트때와 똑같이 mocking 에 대한 부분이었습니다. 스프링부트는내가 직접 생성하고 작성한 클래스나 api들을 mocking 해서 단위 테스트를 진행했으나,jest를 이용할때에는내가 직접 작성한 비즈니스 로직 외에도 npm 으로 설치한 라이브러리까지 mocking 하여 상황별로 given 을 지정해야했기 때문에 ..적응하는데 시간이 꽤 걸렸습니다.
아무래도 해당 부분은 리액트의 동작원리를 파악한다면 더 쉽게 이해할 수 있지 않았을까 하는 생각이 들었습니다.
좋았던점과 아쉬웠던점
좋았던점
프론트엔드로 팀 개발에 참여한게 처음이었지만 전체적으로 능동적으로 행동하시는 팀원분들과 함께해서좋은 첫 스타트를 함께 할 수 있었습니다.
개발 공부를 시작한 이후로, 어디를 가던 팀장 역할을 맡아가면서 매번 저의 자질에 대한 고민을 항상 해왔는데,이번을 계기로 확실히 나는 묻어가는거도 잘하지만 이끄는거도 나쁘지 않게 하고 있구나 라는 생각을 하게 되었습니다.
팀원의 장단점을 잘 파악해 역할 분담을 적절하게 잘한것같습니다.
팀장이라고 무조건 저의 방식대로 강압적으로 리드하지 않고 중립을 지켜가면서 전체적으로 어느 한 분위기에 휩쓸려서 진행되지 않도록 잘 진행한것 같습니다.
전체적으로 실력향상을 할 수 있게끔 핸들링을 한 것 같습니다.
다른 팀원분들께서 어려움을 겪는 부분이 있으실 때 최대한 할 수 있는 부분까지 진행하도록 격려하고 나머지 부분은 제가 도와드리는 방식으로 진행했기 때문에, (라이브코딩) 여러모로 상대방이나 제 자신한테 도움되는 부분이 많았다고 생각했습니다.
아쉬웠던점
최악의 상황에 대한 대비책을 준비해두지 못했던게 아쉬웠습니다.
이번 프로젝트의 경우에는 사상 최초로 잠수를 타는 팀원이 존재했고, 그 팀원을 믿고 해당 파트에 대한 대비책을 준비해두지 않았던 부분이 굉장히 아쉬웠습니다.
그 팀원으로 인해 전체적인 스타일링이 기본 컴포넌트를 구현해 스타일링까지 직접 진행하는걸로 결정되었었지만, 해당 팀원이 제출 당일 저녁시간부터 연락이 되지 않아 급하게 마무리하여 제출하게 되었습니다.
해당 팀원을 탓할수도 있지만,팀장으로써 미리 대비책을 준비해두지 않았던게 굉장히 아쉬웠습니다.프로페셔널한 개발자라면 미리 이러한 상황에 대비하여 진행해야 했다는 생각이 들었습니다.
하지만 이로 인해서 다음 과제부터는불필요한 시간 투자를 줄이고 최대한 해당 과제의 규모에 맞춰서 라이브러리를 선정하여 진행해야겠다는 생각을 하게 되었습니다.
마치며
처음으로 합류해봤던 프론트엔드 교육이라 떨리는 마음으로 임하고 있지만, 1주차 과제를 어찌저찌 끝내고 드는 생각은 아무래도 '시간이 지나고 보면 다 별거 아닌 일들이다' 인 것 같습니다.
2주차 과제는 1주차 과제에서 미흡했던 점, 아쉬웠던 점을 보완하여 욕심 부리지 않고 깔끔하게 마무리 하는 방향으로 가려고 합니다.
오랜만에 작성하는 회고록인만큼 열심히 깔끔하게 작성하려 노력했는데 잘 모르겠네요.. 읽어주셔서 감사합니다!
리액트에서는 기존의 html 태그를 이용하는 것처럼 함수를 선언해 태그처럼 사용할 수 있다.
함수 속에 함수를 선언하거나 외부에서 함수를 불러와서 사용 할 수도 있다.
말그대로 함수형 컴포넌트는 이러한 함수를 컴포넌트로 사용하는것 이라고 생각했다.
컴포넌트가 될 수 있는 부분을 살펴보기
백엔드던 프론트엔드던 가장 좋은 코드는 유지보수가 가능하고 쉬운 코드라고 생각한다.
개인적으로 항상 내가 코딩할 할때 두는 목표는 확장,수정에 힘든 부분이 없도록 설계하는것이다.
서버 개발을 진행할때도 대부분 최대한 한 객체는 하나의 역할만 갖고, 하나의 메소드또한 마찬가지로 하나의 작은 역할을 갖도록 진행했었다.
해당 부분을 단순히 프론트엔드에서 끌어와서 생각해보면 그 하나의 역할만 갖도록 만드는것이 재사용 가능한 컴포넌트를 만드는 작업이라 생각이 되었다.
예를들면 많이 쓰일 수 있는 부분들 (검색창 , 테이블, 버튼, 텍스트 입력창) 같은 부분을 최대한 내부의 코드를 바꿀 필요 없이 요구하는 재료들만 갖다주면 알아서 뿅~ 하고 나오도록 만든다면? 그리고 내부의 코드를 바꿀때도 복잡하게 하나를 변경하면 다른 연결된 무언가를 싹 다 바꿀 필요 없도록 만든다면?
어떠한 홈페이지의 메인 페이지를 구성하다보니, 재사용이 가능한 부분들이 몇개가 있었다.
본인같은 경우에는 메인 페이지에 게임 캐릭터의 랭킹을 표현 할 수 있는 테이블과, 최근 게시글을 출력할 수 있는 테이블 (벌써 이 두개도 하나의 컴포넌트로 재사용이 가능 하겠다는 생각이 들었다.🤓) , 검색창과 헤더, 모바일 메뉴, 로그인창 정도가 있었다.
해당 구성중에 검색창과 테이블, 그리고 로그인창 등에 들어가는 텍스트 필드가 재사용이 가능하겠다는 생각이 들었다.
하나로 예를 든다면, 카테고리와 검색어를 입력하면 검색 결과로 이동하게 해주는 검색창을 만들어버리면 다른 화면에서 매번 새로 커스텀해서 사용할 필요 없이 단순히 스타일만 변경해서 사용한다면 개발 시간이 굉장히 단축된다는 소리이다.
재사용이 애매한 컴포넌트를 재사용이 가능하도록 만들어보기
위에 적어뒀듯이 본인이 진행할 프로젝트에 가장 많이 사용 될 수 있는 부분부터 생각해보면 좋을 것 같다.
본인같은 경우에는 진행하던 프로젝트가 어떠한 게임 랭킹 조회 사이트 + 커뮤니티 사이트 클론코딩 이었기 때문에 랭킹 테이블이 가장 많이 노출될 것 같았다.
여기서도 생각해볼 수 있는게, 어떠한 것을 재사용이 가능한 컴포넌트로 만들것인지가 중요한것같다.
랭킹 테이블자체를 만들것인가?
혹은기본적인 테이블을 만들어서 각 상황에서 커스텀이 가능하도록 꾸밀것인가?
나는 둘다를 선택했다.
왜냐하면 테이블 자체가 여러곳에서 사용이 가능하다. (위에도 언급했듯이 본인의 프로젝트에선 최근 게시판을 출력할때도 사용이 가능했고, 랭킹용으로도 사용이 가능했다. 더 나아가선 마이페이지에서도 사용이 가능할 것 같았다.
랭킹테이블 자체 또한 여러 상황에서 사용이 가능했기 때문에 기본적인 테이블 틀을 만들어 둔 후에, 랭킹용으로 사용 할 수 있는 컴포넌트를 한번 더 만들었다.
처음 스타일은 아니지만, 이런식으로 재사용이 가능하도록 세부 카테고리를 갖고 있는 테이블 컴포넌트를 만들어주었다.이런식으로 기본적인 카테고리를 갖고있는 테이블 컴포넌트를 생성하고, 바디에는 또다른 내용이 들어가도록 자식노드를 넣어주었다.만들어둔 테이블컴포넌트를 이용하여 최근 게시글 테이블을 만들었다.마찬가지로 만들어둔 테이블에 바디부분만 새로 꾸며서 랭킹 테이블을 만들었다.그러면 전체 틀은 테이블 컴포넌트로 꾸며주고, 바디부분만 그리드를 활용해서 꾸며주는 방식으로 2차 활용이 가능하다.
이런식으로 export 되는 컴포넌트들을 하드코딩 하지 않고, 건내주는 props 들을 잘 활용한다면 말그대로 옷입히기 하는것처럼 웹사이트를 완성시킬 수 있다.
굉장히 좋은점은 이 글을 작성하는 지금도, 생각해보니 화살표 버튼을 만들었지만, 해당 버튼을 클릭할때 행동을 정의해두지 않았다.
단순히 건내주는 props 에 boolean 값을 받는 useArrow 와 함수를 받는 onClickArrow 를 추가하여 동작하도록 단 한줄의 코드만 추가하면 끝이었다.
이런식으로 상위 컴포넌트에 옵션을 추가해주도록 선언하면 끝이다.
그러면 전체 틀은 테이블 컴포넌트로 꾸며주고, 바디부분만 그리드를 활용해서 꾸며주는 방식으로 2차 활용이 가능하다. (수치는 수정할 예정이라 보지마세용.)
이 사진만 보더라도 내가 단독으로 고칠 수 있는 부분이 벌써 10개정도 된다.
테이블의 타이틀과 스타일링
화살표의 유무와 행동 방식
카테고리를 사용할지 말지와 카테고리 버튼의 행동과 글씨 스타일링
테이블 내부 콘텐츠 스타일링
순위 아이콘 스타일링
캐릭터 이미지 출력 스타일링
캐릭터 이름/ 모험단 이름/ 서버이름 스타일링
랭킹 수치 스타일링
이정도만 해도 9가지이고, 더 나아가서 스타일링 또한 상위 컴포넌트에서 단독적으로 다루도록 할 수 있게 만든다면 금상첨화인것