🔷 react 컴포넌트에 스타일을 적용시키는 방법 중 하나로, 사용하기 쉽기 때문에 주로 이용된다.
💡 프라그마(Pragma)
- 컴파일러를 위한 주석과 비슷한 개념이다. 자바에서의 애너테이션과 같은 역할이다.
💡 Craco(create-react-app-config-override)
프라그마 없이 정상적인 emotion 적용을 하기 위해 사용한다.
craco.config.js 생성 및 package.json에서의 실행을 craco로 변환하여 사용한다.// craco 추가 명령어 yarn add @craco/craco
// craco.config.js module.exports = { babel: { "presets": ["@emotion/babel-preset-css-prop"], }, }
// package.json 중 scripts 부분 "scripts": { "start": "craco start", "build": "craco build", "test": "craco test", "eject": "craco eject", "storybook": "start-storybook -p 6006", "build-storybook": "build-storybook", "init-msw": "msw init public/" },
import styled from "@emotion/styled";
const Button = styled.button`
diplay: block;
width: 100%;
height: 32px;
padding: 8px 6px;
color: white;
border: none;
border-radius: 4px;
outline: none;
background-color: black;
box-sizing: border-box;
cursor: pointer;
&:hover {
background-color: #111;
}
&:active {
background-color: #222;
}
&:disabled {
background-color: #888;
}
`;
export default Button;
❗ 이전에 실습에 실패했던 가장 큰 이유는 설치의 문제였다. window os의 경우 yarn이 아닌 npm을 이용한 설치를 할 때 VSCode 내에서 오류가 발생했었다. 그렇기 때문에 yarn을 이용한 설치가 필수적이었는데 yarn 명령어가 VSCode 내에서 먹히지 않았다. 이것 역시 window os를 사용할 때 발생하는 문제였다. 파워셸을 관리자 권한으로 실행한 후 정책을 업데이트 한 뒤에 오류 관련 부분을 직접 수정해주어야만 yarn 명령어가 정상 작동한다.
참고했던 곳, 여기서는 정책 업데이트를 하지 않고 진행했음.
🔷 로그인과 회원가입 양식을 만든다.
🔷 useForm 훅을 이용한다.
❗ 위에서 언급한 Craco를 사용하였다. 그래서 이번 실습에서는 프라그마가 등장하지 않는다.
❗ *.stories.js에는 버튼 동작과 관련된 상태 변화 외엔 스타일을 따로 지정할 수 있게 한 것이 없다. 거의 동일한 코드들이므로 하나의 코드만 예시로 두고 나머지는 생략한다.
💻 Button.js
import styled from "@emotion/styled";
const Button = styled.button`
diplay: block;
width: 100%;
height: 32px;
padding: 8px 6px;
color: white;
border: none;
border-radius: 4px;
outline: none;
background-color: black;
box-sizing: border-box;
cursor: pointer;
&:hover {
background-color: #111;
}
&:active {
background-color: #222;
}
&:disabled {
background-color: #888;
}
`;
export default Button;
💻 Button.stories.js
import Button from "../components/Button";
export default {
title: 'Components/Button',
component: Button,
argTypes: {
onClick: { action: "onClick" }
}
};
export const Default = (args) => {
return <Button {...args}>Button</Button>;
};
🖨 버튼 컴포넌트
💻 Title.js
import styled from "@emotion/styled";
const Title = styled.h1`
font-size: 24px;
text-align: center;
`;
export default Title;
🖨 타이틀 컴포넌트
💻 CardForm.js
import styled from "@emotion/styled";
const CardForm = styled.form`
padding: 16px;
width: 400px;
background-color: white;
box-shadow: 0 0 4px rgba(0, 0, 0, 0.2);
box-sizing: border-box;
`;
export default CardForm;
🖨 카드 양식 컴포넌트
💻 ErrorText.js
import styled from "@emotion/styled";
const ErrorText = styled.span`
font-size: 12px;
color: red;
`
export default ErrorText;
🖨 에러메시지 컴포넌트
💻 Input.js
import styled from '@emotion/styled';
const Input = styled.input `
display: block;
padding: 4px 6px;
width: 100%;
height: 28px;
font-size: 14px;
border-radius: 5px;
border: 2px solid #360;
background-color: white;
`
export default Input;
🖨 입력창 컴포넌트
💻 useForm.js(Hook)
import { useState } from "react";
const useForm = ( { initialValues, onSubmit, validate } ) => {
const [values, setValues] = useState(initialValues);
const [errors, setErrors] = useState({});
const [isLoading, setIsLoading] = useState(false);
const handleChange = (e) => {
const { name, value } = e.target;
setValues({ ...values, [name]: value })
};
const handleSubmit = async (e) => {
setIsLoading(true);
e.preventDefault();
const newErrors = validate(values);
if(Object.keys(newErrors).length === 0) {
await onSubmit();
}
setErrors(newErrors);
setIsLoading(false);
}
return {
values,
errors,
isLoading,
handleChange,
handleSubmit
}
};
export default useForm;
💻 LoginForm.js
import Input from "./Input";
import Button from "./Button";
import useForm from "../hooks/useForm";
import ErrorText from "./ErrorText";
import CardForm from "./CardForm";
import Title from "./Title";
const LoginForm = ( { onSubmit }) => {
const { errors, isLoading, handleChange, handleSubmit } = useForm({
initialValues: {
name:'',
password: ''
},
onSubmit,
validate: ({ name, password }) => {
const newErrors = {};
if(!name) newErrors.name = '아이디를 입력해주세요.';
if(!password) newErrors.password = "비밀번호를 입력해주세요.";
return newErrors;
}
})
return (
<CardForm onSubmit={handleSubmit}>
<Title>로그인</Title>
<Input type="text" name="id" placeholder="아이디" onChange={handleChange}/>
{errors.name && <ErrorText>{errors.name}</ErrorText>}
<Input type="password" name="password" placeholder="비밀번호" onChange={handleChange} style={{marginTop: 8}}/>
{errors.password && <ErrorText>{errors.password}</ErrorText>}
<Button type="submit" disabled={isLoading} style={{marginTop: 16}}>로그인</Button>
</CardForm>
);
};
export default LoginForm;
🖨 로그인 양식 컴포넌트
💻 SignUpForm.js
import Input from "./Input";
import Button from "./Button";
import useForm from "../hooks/useForm";
import ErrorText from "./ErrorText";
import CardForm from "./CardForm";
import Title from "./Title";
const SignUpForm = ({ onSubmit }) => {
const { errors, isLoading, handleChange, handleSubmit } = useForm({
initialValues: {
name:'',
password: '',
passwordConfirm: '',
},
onSubmit,
validate: ({ name, password, passwordConfirm }) => {
const newErrors = {};
if(!name) newErrors.name = '아이디를 입력해주세요.';
if(!password) newErrors.password = "비밀번호를 입력해주세요.";
if(password !== passwordConfirm) newErrors.passwordConfirm = "비밀번호가 일치하지 않습니다.";
return newErrors;
}
})
return (
<CardForm onSubmit={handleSubmit}>
<Title>회원 가입</Title>
<Input type="text" name="id" placeholder="아이디" onChange={handleChange}/>
{errors.name && <ErrorText>{errors.name}</ErrorText>}
<Input type="password" name="password" placeholder="비밀번호" onChange={handleChange} style={{marginTop: 8}}/>
{errors.password && <ErrorText>{errors.password}</ErrorText>}
<Input type="password" name="password-confirm" placeholder="비밀번호 확인" onChange={handleChange} style={{marginTop: 8}}/>
{errors.passwordConfirm && <ErrorText>{errors.passwordConfirm}</ErrorText>}
<Button type="submit" disabled={isLoading} style={{marginTop: 16}}>회원 가입</Button>
</CardForm>
);
}
export default SignUpForm;
🖨 회원가입 양식 컴포넌트
스토리북과 이모션을 활용하는 법을 익히기 위해 실습해보았다.
이제 컴포넌트에 좀 더 익숙해지기 위한 연습을 해야겠다.