각 컴포넌트들을 먼저 만들고 나중에 합치는 방식으로 만들 예정임. -> 나중에 다른 페이지에서도 해당 컴포넌트를 재사용하기 위함.
일단 컴포넌트들을 넣어둘 폴더를 만든다.
mern_project/mern_client/src 안에 components라는 이름의 폴더를 생성한다.
그리고 그 아래에 일반적인 컴포넌트들을 넣을 common 폴더를 생성한다.
VScode에서 리액트를 사용할 때
react라고 검색하면 제일 위에 뜨는 익스텐션을 깔면 아주 쉽게..단축어로..코드를 짤 수 있다...

이겅
common 폴더 안에 Block.tsx 파일을 생성한다.
rfce라고 입력하면 기본 뼈대가 자동으로 완성됨.
import React, {memo} from "react";
import styled from "styled-components";
// width: 100%
// height: props.height
// onClick: props.onClick
interface BlockProps {
height: string;
onClick?: () => void; //optional하다는 뜻
}
const StyledBlock = styled.div<BlockProps>`
width: 100%;
height: ${(props) => props.height};
cursor: ${(props) => props.onClick && "pointer"}; //해당 컴포넌트를 마우스로 선택 가능하게 해줌
`;
function Block({height, onClick}: BlockProps) {
return <StyledBlock height={height} onClick={onClick} />;
}
export default memo(Block); //리렌더링 불가
동일하게 common 폴더 안에 Button.tsx 파일을 생성한다.
default 버튼, link 버튼 두 개를 만들 예정
import React from "react";
import styled from "styled-components";
import { Link } from "react-router-dom";
import { memo } from "react";
// children
// onClick
// type: 'link' | 'button'
// url
interface ButtonProps {
children?: React.ReactNode;
onClick?: (e: any) => void;
type: "link" | "button";
url?: string;
}
const StyledButton = styled.button<ButtonProps>`
outline: none;
border: none;
display: flex;
align-items: center;
justify-content: center;
background: none;
padding: 0;
cursor: pointer;
`;
function Button({ children, onClick, type="button", url }: ButtonProps) {
// type에 따라서 link button인지 default 버튼인지 분리해서 사용 -> 각각의 컴포넌트를 분리해서 정의
const RealButton = (
<StyledButton onClick={onClick}>
{children}
</StyledButton>
);
const RealLink = (
<StyledButton>
<Link to={url!}>{children}</Link> //react는 a태그보다는 link태그를 더 많이 사용한다 / 느낌표는 해당 옵션이 필수로 들어올 것이라는 표시
</StyledButton>
);
return type === "link" && url ? RealLink : RealButton;
}
export default memo(Button);
(e: any)에서 e는 event다...
동일하게 common 폴더 안에 Divider.tsx 파일을 생성한다.
import React from 'react';
import styled from 'styled-components';
import { memo } from 'react';
// width
// height
interface DividerProps {
width?: string;
height?: string;
}
const StyledDivider = styled.div<DividerProps>`
width: ${(props) => (props.width ? props.width : "1px")};
height: ${(props) => (props.height ? props.height : "20px")};
opacity: 0.2;
background: #707070;
margin: 0 8px;
`;
function Divider({width, height}: DividerProps) {
return <StyledDivider width={width} height={height} />;
}
export default memo(Divider);
Span.tsx 파일 생성
import React from 'react';
import styled from 'styled-components';
import { memo } from 'react';
// children
// size : 'small' | 'normal' | 'title'
// color : string
interface SpanProps {
children?: React.ReactNode;
size?: 'small' | 'normal' | 'title';
color?: string;
}
const StyledSpan = styled.span<SpanProps>`
color: ${(props) => props.color || "black"};
&.small {
font-size: 0.8rem;
} // &기호는 StyledSpan 컴포넌트를 가리킨다
&.normal {
font-size: 1rem;
}
&.title {
font-size: 2rem;
font-weight: bold;
}
`;
function Span({children, size = "normal", color}: SpanProps) {
return (
<StyledSpan className={size} color={color}>
{children}
</StyledSpan>
);
}
export default memo(Span)
ShadowBox.tsx 파일 생성
검색창 박스 만드는거
import React from 'react'
import styled from 'styled-components';
import { memo } from 'react';
// children
interface ShadowBoxProps {
children?: React.ReactNode;
}
const StyledShadowBox = styled.div`
display: flex;
align-items: center;
position: absolute;
top: 16px;
left: 16px;
right: 16ps;
max-width: 400px;
border-radius: 10px;
padding: 6px 8px;
box-shadow: rgb(0 0 0 / 16%) 0px 3px 6px 0px;
border: 1px solid #e8e8e8;
box-sizing: border-box;
z-index: 101; // navermap 100
background: #ffffff
`;
function ShadowBox({children}: ShadowBoxProps) {
return (
<StyledShadowBox>{children}</StyledShadowBox>
);
}
export default memo(ShadowBox)
Input.tsx 파일 생성
enter 키를 눌렀을 때 뭘 반환하게 만들기
import React from 'react'
// children
// name
// value
// onChange
// onSubmit
interface InputProps {
children?: React.ReactNode;
name?: string;
value?: string;
onChange?: (e: any) => void;
onSubmit?: () => void;
}
const StyledInput = styled.input<InputProps>`
display: inline-block;
border: none;
width: 100%;
min-height: 2em;
font-size: 14px;
`;
function Input({children, name, value, onChange, onSubmit}: InputProps) {
const onEnterSubmit = (e:any) => {
if (!onSubmit) return;
if (e.key == "Enter") {
onSubmit();
}
}
return (
<StyledInput name={name} value={value} onChange={onChange} onKeyDown={onEnterSubmit}>
{children}
</StyledInput>
)
}
export default memo(Input)
위의 요소들에서 모두 이벤트 타입을 (e: any)로 설정해 놓았는데, 이렇게 하면 타입을 strict하게 정의해 놓은 것이 아니기 때문에 에러가 생길 확률도 높고 생각한대로 코드가 작동하지 않을 수도 있다...라고한다..
일단
Button.tsxonClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
로 수정을 해주는데, 복붙하라고 해놓고는 약간 수정을 해야하긴 하네...
알아서 잘 하면 되는건가보다.
Input.tsxonChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
onEnterSubmit의 경우에도
const onEnterSubmit = (e:React.KeyboardEvent<HTMLInputElement>) => {
if (!onSubmit) return;
if (e.key == "Enter") {
onSubmit();
}
}
이렇게 수정해준다.