동아리 홈페이지 프로젝트의 코드 가독성을 높이기 위한 전략
이번에는 프로젝트의 코드 가독성을 높이기 위해 몇 가지 전략을 시도해보았다. "동아리" 홈페이지라는 특성 상, 이 프로젝트는 동아리가 살아있는 동안은 계속 존재하고 유지보수되어야 한다. 이 말은 "내가 이 프로젝트에서 나가고 다른 개발자가 맡았을 때 이해하기 쉬워야 한다"는 뜻이다. 이러한 시도가 효과적인지는 조만간 후배가 들어왔을 때 알 수 있을 것이다.
우리가 택한 전략은 다음과 같다.
- TypeScript
- TSDoc
- 시멘틱 태그
1. TypeScript
사실 이러한 이유 때문에 기존의 JS 프로젝트를 TS로 마이그레이션했다. 타입도 없는 레거시를 다른 개발자가 이어갈 수 있을까 의문이 들었고, 그래서 정적 타입가 가능한 TypeScript로 옮기기로 했다.
또한 각기 다른 위치에 커스텀 타입 선언이 생겨나서 관리가 힘들어지는 문제를 방지하기 위해 타입 관련 파일을 분리시켰다. @types 폴더 내에 declare 문법으로 타입을 선언했다. 절대 경로 import 하기 위해 ".d.ts" 확장자를 사용했다.
- src/@types/
// src/@types/router.d.ts
declare module 'router' {
export type RouteItem = {
id: number;
path: string;
label: string;
withAuth: boolean;
element: JSX.Element;
};
}
// src/@types/form.d.ts
declare module 'form' {
export interface InputFormProps {
type: string;
name: string;
label: string;
value: string;
error: boolean;
onChange: (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
onBlur: (e: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
}
export interface UseFormProps {
initialValues: UserForm;
validate: (values: UserForm) => FormState;
onSubmit: (values: UserForm) => void;
}
export interface UserForm {
[key: string]: string;
name: string;
major: string;
number: string;
email: string;
phone: string;
url: string;
bio: string;
}
export interface FormState {
[key: string]: boolean;
}
}
2. TSDoc
TypeScript 코드의 문서화를 위해 TSDoc을 도입했다. 함수를 작성한 나에게는 어떤 기능을 수행하는 함수인지 명확하지만, 이 함수를 처음보는 개발자에게는 어떤 기능을 수행하는지, 어떤 인자를 받아서 어떤 반환값을 리턴하는지, 구조는 어떤지 바로 알 수는 없을 것이다. 특히나 커스텀 훅의 경우, state와 같은 훅을 사용하여 더욱 복잡한 로직을 가지기에 이해하는 데에 시간이 걸릴 것이다. 이런 가독성 문제를 해결하기 위해 TSDoc을 적극 사용하였다.
// src/hooks/useForm.ts
/**
* 폼 입력 처리를 위한 커스텀 훅입니다.
*
* 이 훅은 폼의 상태 관리를 쉽게 하기 위해 사용됩니다. 입력 값 관리, 유효성 검사,
* 그리고 폼 제출 처리를 위한 로직을 포함하고 있습니다.
*
* @param initialValues - 폼의 초기값을 지정하는 객체입니다.
* @param validate - 입력 값에 대한 유효성 검사 함수입니다.
* @param onSubmit - 폼 제출 시 호출되는 콜백 함수입니다.
* @returns 폼의 값(values), 오류 메시지(errors), 터치된 필드(touched),
* 이벤트 핸들러(handleChange, handleBlur, handleSubmit)를 반환합니다.
*
* @example
* const form = useForm({
* initialValues: { name: '', email: '' },
* validate: (values) => {
* const errors = {};
* if (!values.name) {
* errors.name = 'Name is required';
* }
* if (!values.email) {
* errors.email = 'Email is required';
* }
* return errors;
* },
* onSubmit: (values) => {
* console.log(values);
* },
* });
*
* // 사용 예시
* <form onSubmit={form.handleSubmit}>
* <input
* name="name"
* value={form.values.name}
* onChange={form.handleChange}
* onBlur={form.handleBlur}
* />
* {form.touched.name && form.errors.name && <div>{form.errors.name}</div>}
* <input
* name="email"
* value={form.values.email}
* onChange={form.handleChange}
* onBlur={form.handleBlur}
* />
* {form.touched.email && form.errors.email && <div>{form.errors.email}</div>}
* <button type="submit">Submit</button>
* </form>
*/
function useForm({ initialValues, validate, onSubmit }: UseFormProps) {
//...
}
3. 시멘틱 태그
웹페이지 구조의 의미를 명확하게 할 수 있는 시멘틱 태그를 도입했다. 이전까지는 상단바, 하단바, 네비게이터, 텍스트, 목차 등을 구현할 때 모조리 div를 사용했다. 사실 div를 사용하면서도 전부 div만 써도 되나.. 의구심이 들긴 했지만 개선하지는 않았다. header나 nav 태그를 특별한 기능이 있는 태그로 알고 있어서 사용하지 않았는데, 자세히 알아보니 이 태그들은 특별한 기능을 수행하지 않는 "시멘틱 태그"인 것을 알게 되었다.
시멘틱 태그는 그 자체로 의미를 갖기 때문에, 코드를 보는 사람들에게 각 요소의 역할을 명확하게 알려준다. 또한 시멘틱 태그를 사용함으로써 검색 엔진이 웹페이지의 구조를 더 잘 인덱싱한다. 이를 통해 SEO를 높인다.
- src/components/common/Header.tsx
// src/components/common/Header.tsx
function Header() {
const location = useLocation();
const { scrollYProgress } = useScroll();
const headerData = routerData.filter((item) => isNotAuthPage(item.path));
return (
<>
<header className="fixed z-10 w-full h-16 bg-white">
<div className="flex items-center w-full h-full p-7">
{/* TODO: 로고 이미지로 대체해야함. */}
<h3 className="flex justify-start flex-1" id="aid-logo">
AI Developers
</h3>
<nav className="flex items-center grow justify-evenly">
{headerData.map((item) => (
<div className="" key={item.label}>
<Link to={item.path}>
<Button label={item.label} />
</Link>
</div>
))}
</nav>
<div className="flex justify-end flex-1 gap-4 w-1/8">
<button>Login</button> | <button>Register</button>
</div>
</div>
<motion.div
className="absolute bottom-0 w-full h-1"
style={{
scaleX: scrollYProgress,
transformOrigin: 'left',
backgroundColor: isDarkPage(location.pathname)
? colors.primary
: 'black',
}}
/>
</header>
</>
);
}
export default Header;
- 일반적인 컴포넌트의 경우 굳이 tsdoc을 작성하지 않는다.
- 컴포넌트 이름에서 이름과 역할을 유추할 수 있기 때문.
결론
코드 가독성을 높이기 위해 여러 방법을 사용했지만, 걱정되는 부분도 존재한다. 프로젝트에 여러 전략이 사용될수록 후에 들어올 개발자가 "알아야 할 것"이 많아졌다. 타입스크립트를 제대로 사용하기 위해서는 @types 폴더와 declare에 대해 알아야하고, tsdoc은 무엇이고 어떻게 작성하는지, 시멘틱 태그는 일반적인 태그랑 무엇이 다르고 왜 쓰는지..
후배 개발자가 배워야 할 것들이 많아져서 진입장벽을 높게 친 것 같아서 조금 걱정이다. 이런 문제를 해결하기 위해서는 동아리 내 프론트엔드 커리큘럼을 개선해야 할 것 같다.
'프로젝트 > 인공지능 동아리, AID' 카테고리의 다른 글
[AID] Zero-shot 기반의 채용공고 필터링 디스코드 훅 (0) | 2024.03.11 |
---|---|
AID 모각코 다녀옴 (0) | 2024.01.23 |
[팀프로젝트] AID 홈페이지 리뉴얼 - (3) API 모킹 (3) | 2024.01.22 |
[팀프로젝트] AID 홈페이지 리뉴얼 - (1) 기획 및 큰 틀 구현 (0) | 2023.12.16 |