23.07.08
오늘 한 일
- 프로그라피 세션 참여
- Gloddy 개발
🤔생각
한재엽님의 변경에 유연한 컴포넌트 글을 보았다. 예전에 읽었을 땐 와닿지 않았는데, 다시 읽어보니 많은 생각을 하게 되는 글이다.
나만의 철학을 가지며 코드를 작성하자
이제까지 프론트엔드 개발을 하면서 많은 프로젝트들, 문서들을 참고해서 코드를 작성해나갔다.
하지만 이제는 나만의 철학, 주관을 가지고 코드를 작성해야 된다고 생각한다.
다른 사람들의 코드를 보면 각자 작성하는 스타일이 다 다르다. 협업을 하면 나와 다른 스타일을 가진 사람들과 코드를 작성해야 한다.
내가 어떤 스타일로, 어떤 철학으로 코드를 작성하는지 알고 그걸 설명할 수 있다면 협업 시 컨벤션을 맞추는데 도움이 될 것이다.
좋은 코드란?
멋있고 화려한 코드가 아닌, 남들이 읽었을 때 잘 읽히고 당연하다고 느껴지는 코드가 좋은 코드
이다. 당위성이 있어야 한다.
그러기 위해서는 추상화
를 잘 해야한다. 추상화를 통해 코드를 단순화하고, 당연하게 만들어야 한다.
오늘 읽은 글
Gloddy 개발
ConfirmModal 컴포넌트 수정
어제 이어서 오늘 ConfirmModal 컴포넌트를 수정했다.
Compound Component 패턴을 사용하지 않고 구현했다.
framer-motion
을 사용해서 모달이 열릴 때 애니메이션을 주었다.
ModalWrapper
컴포넌트에서 framer-motion
을 사용했다.
그리고 모달의 뒷 배경을 클릭 했을 때 닫히게 하기 위해 useOnClickOutside
훅을 만들었다.
import { useEffect } from 'react';
export function useOnClickOutside<T extends HTMLElement = HTMLElement>(
ref: React.RefObject<T>,
handler: (event: MouseEvent | TouchEvent) => void
) {
useEffect(() => {
const listener = (event: MouseEvent | TouchEvent) => {
event.stopPropagation();
if (!ref.current || ref.current.contains(event.target as Node)) {
return;
}
handler(event);
};
document.addEventListener('mousedown', listener);
document.addEventListener('touchstart', listener);
return () => {
document.removeEventListener('mousedown', listener);
document.removeEventListener('touchstart', listener);
};
}, [ref, handler]);
}
최종 구현 코드
// ModalWrapper.tsx
import { useRef } from 'react';
import { motion } from 'framer-motion';
import { useOnClickOutside } from '@/hooks/useOnClickOutside';
import PortalWrapper from '../PortalWrapper';
import type { StrictPropsWithChildren } from '@/types';
interface ModalWrapperProps {
isOpen: boolean;
onClose?: () => void;
}
export default function ModalWrapper({
isOpen,
onClose = () => {},
children,
}: StrictPropsWithChildren<ModalWrapperProps>) {
const modalRef = useRef<HTMLDivElement>(null);
useOnClickOutside(modalRef, onClose);
return (
<PortalWrapper isShow={isOpen}>
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}>
<div className="fixed left-1/2 top-0 z-10 h-full w-full max-w-450 -translate-x-1/2 bg-[rgba(0,0,0,0.4)]">
<div
ref={modalRef}
className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2"
>
{children}
</div>
</div>
</motion.div>
</PortalWrapper>
);
}
// ConfirmModal.tsx
import Button, { ButtonProps } from '@/components/common/Button';
import ModalWrapper from './ModalWrapper';
import type { PropsWithChildren } from 'react';
interface ConfirmModalProps {
isOpen: boolean;
okText: ButtonProps['text'];
cancelText: ButtonProps['text'];
okColor?: ButtonProps['color'];
cancelColor?: ButtonProps['color'];
onClickOk?: () => void;
onClickCancel?: () => void;
}
export default function ConfirmModal({
isOpen,
onClickOk,
onClickCancel,
okText = '네',
cancelText = '아니요',
okColor = 'blue',
cancelColor = 'gray',
children,
}: PropsWithChildren<ConfirmModalProps>) {
return (
<ModalWrapper isOpen={isOpen} onClose={onClickCancel}>
<div className="w-300 rounded-10 bg-white px-16 pb-15 pt-30">
<div className="mb-20 flex flex-col items-center text-center">{children}</div>
<div className="flex flex-col gap-8">
<Button onClick={onClickOk} text={okText} color={okColor} />
<Button onClick={onClickCancel} text={cancelText} color={cancelColor} />
</div>
</div>
</ModalWrapper>
);
}
내일 할 일
- 타입스크립트 스터디 계획
- Rust 소유권 공부
- CS 스터디 공부