🚩 목표
☑️ Input 컴포넌트
☑️ TS 적용
☑️ LinkBox 컴포넌트
💡 시작하기
- 경로 : src → components
- base(작은 단위)와 domain(base들의 합 또는 큰 단위)로 구분함.
🔸 Input 컴포넌트
import { CSSProperties } from 'react'; import styled from '@emotion/styled'; interface InputProps { style?: CSSProperties; type: 'text' | 'number' | 'password'; } const Input = ({ type, ...props }: InputProps) => { return <InputStyled type={type} style={{ ...props.style }} {...props} />; }; export default Input; const InputStyled = styled.input` background: none; border: solid 1px #c4c4c4; border-radius: 8px; font-size: 16px; transition: border 0.2s; &:hover { border: solid 1px black; } `;
interface로 props 지정하기
- 기본으로 지정한 style외에 다른 style을 추가할 수 있도록 style을 prop로 받음. 이때
CSSProperties
를 지정하면 모든 스타일을 type으로 지정할 수 있음. - 물음표를 사용하면 입력을
선택
으로 만들 수 있음.
- Input의 type은 여러가지가 올 수 있기 때문에 prop과 or 연산자를 이용하여 만듦.
- 이 외의 다른 prop을 적용할 수 있도록 spread 연산자를 사용함.
border의 색상 등 상태로 관리해야 할 것들이 있음.
이벤트를 줘야 할까?
- Form hook에서 이벤트를 조작하고 있기 때문에 컴포넌트는 순수하게 가져가기로 결정.
🔹 LinkBox 컴포넌트
import { ChevronRight } from 'react-feather'; import styled from '@emotion/styled'; import React, { CSSProperties } from 'react'; interface LinkBoxProps { children: React.ReactNode; src?: string; style?: CSSProperties; } const checkHasImage = (str: string | undefined) => { return str; }; const LinkBox = ({ children, src, ...props }: LinkBoxProps) => { return ( <LinkContainer style={{ ...props.style }} {...props}> {checkHasImage(src) && <LinkImg src={src} />} <div> <LinkText>{children}</LinkText> <ChevronRight size={40} /> </div> </LinkContainer> ); }; export default LinkBox; const LinkContainer = styled.div` display: flex; align-items: center; border: solid 1px #c4c4c4; border-radius: 16px; padding: 10px 20px; color: black; margin-bottom: 10px; transition: border 0.2s; & > div { display: flex; justify-content: space-between; align-items: center; width: 100%; } &:hover { border: solid 1px black; } `; const LinkText = styled.span` font-size: 20px; `; const LinkImg = styled.img` min-width: 80px; min-height: 80px; max-width: 80px; max-height: 80px; border-radius: 16px; background-color: black; margin-right: 20px; `;
Img를 선택적으로 넣게 만들기
- LinkBox prop으로 src 삽입을 선택으로 만듦.
- 이미지의 유무에 따라 이미지 태그를 보여줄지 결정하는 함수 제작.
hasImage
보단checkHasImage
를 사용하기 → 문법적으로 틀릴순 있어도이미지를 가지고 있다
라는 의미보다이미지를 가지고 있는지 체크한다
가 더 직관적이기 때문.- str을 바로 return 하면 str이 존재할 때 true를, 없을 때 false를 반환함.
- 이때 type으로 string 또는 undefined를 전달함. 매개 변수가 필수가 아니기 때문에 undefined를 넣어줘야 함.
- 만약 매개변수가 boolean 값이라면 함수를 거치지 않고 and 연산자로만 코드를 작성할 수 있음.
- Img를 선택적으로 넣게 만들었기 때문에 이미지가 있는 LinkBox와 없는 LinkBox를 별도로 만들 필요가 없어졌음.
height 지정하기 🤔
- min, max로 각각 지정해주어야 image가 밀리지 않음.
width
,height
로만 지정하면 image가 나란히 있는 UI 조각에 의해 밀림.
children의 의미
props.children
element output으로 직접 전달할 수 있음.
- 구성 요소를 호출 할 때
{}
에 포함된 콘텐츠를 렌더링할 때 사용함.
function FancyBorder(props) { return ( <div className={'FancyBorder FancyBorder-' + props.color}> {props.children} </div> ); }
- children prop의 type을 정의하는 방법 3가지
JSX.Elemen
: single React element일 때 사용하기 좋음. 하지만 JSX.Element는 string을 지원하지 않기 떄문에 다음과 같이 작성해야 함.
children: | JSX.Element | JSX.Element[] | string | string[];
ReactChild
: JSX.Element는 string을 지원하기 위해 추가적으로 type을 작성하지만 만약 number도 추가하고 싶다면 복잡해진다는 단점이 있음. 반면 ReactChild
는 React elements, strings, numbers 모두 포함함.ReactNode
: ReactChild는 모든 것을 포함하지만 너무 광범위(verbose)함. ReactNode가 좀 더 간결하지만 multiple elements, strings, numbers, fragments, portals 등 대부분의 것을 포함함.🤩 적용하기

🛠️ 리펙토링
Input
- 사실 Input 컴포넌트의 경우 항상 고정값으로 들어가는 스타일 때문에 컴포넌트로 제작하였음.
- 하지만 JS 기능을 붙일 때 컴포넌트와 호환이 되는지를 항상 확인해야 하는 단점이 있음.
- 이럴 땐 styled를 적용한 input 태그만 export 해줘도 됨.
import styled from '@emotion/styled'; const Input = styled.input` background: none; border: solid 1px #c4c4c4; border-radius: 8px; font-size: 16px; transition: border 0.2s; &::-webkit-outer-spin-button, ::-webkit-inner-spin-button { -webkit-appearance: none; } &:hover { border: solid 1px black; } `; export default Input;
Button
- Button은 onClick, onSubmit 등 다양한 이벤트를 등록하는 경우가 많음.
- 이를 고정시키고 싶은 게 아니라면 컴포넌트를 사용하는 곳에서 자유롭게 쓸 수 있도록 해주는 것이 좋음.
- 그렇게 하기 위해
extends React.ButtonHTMLAttributes<HTMLButtonElement>
로 Props를 확장시킬 수 있음. extends
으로 props의 부분 집합을 의미함.HTMLButtonElement
는 버튼 요소를 조작할 수 있는 속성을 제공함.
import styled from '@emotion/styled'; import React from 'react'; interface Props extends React.ButtonHTMLAttributes<HTMLButtonElement> { children: React.ReactNode; type: 'button' | 'submit' | 'reset'; } const Button = ({ children, type, ...props }: Props) => { return ( <StyledButton type={type} style={{ ...props.style }} {...props}> {children} </StyledButton> ); }; const StyledButton = styled.button` background: linear-gradient(270deg, #b88bd6 0%, #b88bd6 0.01%, #a8bac8 100%); border: none; cursor: pointer; `; export default Button;