Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MISSION3 / 곽민준 #26

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,794 changes: 3,794 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"styled-components": "^6.1.8"
},
"devDependencies": {
"@types/react": "^18.2.64",
Expand Down
1 change: 0 additions & 1 deletion public/vite.svg

This file was deleted.

152 changes: 117 additions & 35 deletions src/App.css
Original file line number Diff line number Diff line change
@@ -1,42 +1,124 @@
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
html,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사용하지 않는 태그도 포함해서 스타일 속성을 부여한 이유가 궁금합니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CSS초기화를 하고 시작해야할 거 같아서 블로그 게시글을 보고 코드를 붙여넣었는데, 잘못된 방법일까요?!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

궁금해서 여쭤봤어요ㅎㅎ 이런 것도 있네요~ 하나 알아갑니다ㅎㅎ

body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}

.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
/* HTML5 display-role reset for older browsers */
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
display: block;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
body {
line-height: 1;
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
ol,
ul {
list-style: none;
}

@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
blockquote,
q {
quotes: none;
}

@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
blockquote:before,
blockquote:after,
q:before,
q:after {
content: "";
content: none;
}

.card {
padding: 2em;
}

.read-the-docs {
color: #888;
table {
border-collapse: collapse;
border-spacing: 0;
}
46 changes: 16 additions & 30 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,21 @@
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
import React from "react";
import styled from "styled-components";
import CardRegisterForm from "./components/CardRegisterForm";
import "./App.css";

function App() {
const [count, setCount] = useState(0)

return (
<>
<div>
<a href="https://vitejs.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
<p>
Edit <code>src/App.jsx</code> and save to test HMR
</p>
</div>
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
</>
)
<GreyBackground>
<CardRegisterForm />
</GreyBackground>
Comment on lines +8 to +10
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사람은 위에서 아래로 내리면서 보기 때문에 일반 컴포넌트와 styled 컴포넌트에 대한 컴포넌트 명을 구분할 수 있다면 가독성이 더 좋아질 것 같아요ㅎㅎ

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저 역시도 제 코드를 읽을 때, 스타일드 컴포넌트인지 일반 컴포넌트인지 구분하기가 어려웠는데 이름에 차별화를 두면 구분이 훨씬 쉽겠네요!!!

);
}

export default App
export default App;

const GreyBackground = styled.div`
background-color: #efeff0;
height: 100vh;
display: flex;
justify-content: center;
`;
1 change: 0 additions & 1 deletion src/assets/react.svg

This file was deleted.

29 changes: 29 additions & 0 deletions src/components/Card.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from "react";
import styled from "styled-components";

const Card = ({ cardNumber }) => {
return (
<CardList>
<CardImage />
<CardNumber>카드 번호: {cardNumber}</CardNumber>
</CardList>
);
};

export default Card;

const CardImage = styled.div`
width: 9.375rem;
height: 5rem;
background-color: white;
border-radius: 0.3125rem;
`;

const CardList = styled.div`
display: flex;
padding: 0.625rem 0.625rem 0.625rem 0rem;
`;

const CardNumber = styled.div`
padding: 0.625rem;
`;
26 changes: 26 additions & 0 deletions src/components/CardNumberInputs.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// CardNumberInputs 컴포넌트
import React from "react";
import DataInput from "./DataInput";

const CardNumberInputs = ({ cardNumbers, handleInputChange }) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CardRegisterForm에서 DataInput을 바로 사용하지 않고 CardNumberInputs 컴포넌트를 따로생성한 이유가 있을까요??
해당 기능만 있다면 아래와 같이 구현할 수도 있을 것 같아요ㅎㅎ

Suggested change
const CardNumberInputs = ({ cardNumbers, handleInputChange }) => {
// CardRegisterForm.jsx
<StyledCardNumberInputs>
{cardNumbers.map((number, index) => (
<DataInput
key={index}
placeholder={"0000"}
value={number}
onChange={handleInputChange(index)}
/>
))}
</StyledCardNumberInputs>
const StyledCardNumberInputs = styled.dev`
width: 100%;
display: flex;
justify-content: space-between;
`

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

다른 부분과 카드 입력하는 부분이 뭔가 다르게 구성되어져있다고 생각해서 따로 만들었는데, 이런식으로 하면 컴포넌트를 추가로 만들 필요 없이 재사용성을 높일 수 있겠네요! 이렇게 컴포넌트 하나로만 해보고싶었는데 어떻게 해야 할 지 막막했는데 알려주셔서 감사합니다! 다시 만들어보도록 하겠습니다

return (
<div
style={{
display: "flex",
width: "100%",
justifyContent: "space-between",
}}
>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

일관성있게 styled 컴포넌트로 구현해주시면 좋을 것 같은데, style을 인라인으로 삽입한 이유가 있을까요??

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저 당시에 제가 왜 저렇게 작성했는지 모르겠네요ㅠㅠ 일관성 있게 작성하도록 하겠습니다!

{cardNumbers.map((number, index) => (
<DataInput
key={index}
placeholder={"0000"}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

react에서는 변수가 아니여도 {} 안에 넣는게 자연스러운걸까요??

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

변수나 표현식을 사용해야 할 경우에는 중괄호를 사용해야 하지만, 속성값이 정적인 문자열이라면, 바로 값을 할당하는 것이 좋겠네요!

value={number}
onChange={handleInputChange(index)}
/>
))}
</div>
);
};

export default CardNumberInputs;
69 changes: 69 additions & 0 deletions src/components/CardRegisterForm.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import DataInput from "./DataInput";
import React, { useState } from "react";
import styled from "styled-components";
import Card from "./Card";
import CardNumberInputs from "./CardNumberInputs";
Comment on lines +1 to +5
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

외부 라이브러리 import와 내부 파일 import는 각각 묶어주시는게 조금 더 보기 좋을 것 같아요ㅎㅎ

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import React, { useState } from "react";
import styled from "styled-components";

import DataInput from "./DataInput";
import Card from "./Card";
import CardNumberInputs from "./CardNumberInputs";

이런식으로 내부 파일 사용하는 것과 외부 라이브러리 import하는 것을 나누면 훨씬 가독성이 높겠네요! 감사합니다


const CardRegisterForm = () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CardRegisterForm에 대해 몇가지 제안드리고 싶은게 있습니다.

  1. 카드 정보객체를 state로 만들어 관리
    카드 정보를 저장하는 목적의 form이기 때문에 카드이름, 번호, cvc 등 카드 정보를 객체로 만들어 관리하는게 해당 컴포넌트의 목적에 좀 더 자연스러운 방향일 것 같습니다.
  2. form, button 태그의 기본 속성에 집중
    각 input에 name 속성을 추가하고, onChange 핸들러를 삽입하면, 이벤트로 valuename을 가져올 수 있습니다. 이 값으로 1번에서 선언해놓은 카드정보 객체의 값을 업데이트한다면 사용자의 input 액션에 따라 카드 정보를 알맞게 업데이트할 수 있을 것 같아요ㅎㅎ 그리고, form 태그를 FormTitle과 같은 레벨에 삽입하고 onSubmit 핸들러를 추가한다음, 완료 button에 submit type을 추가한다면 완료 버튼을 클릭할 때마다 카드 정보객체의 데이터를 Card 컴포넌트에 던져서 저장할 수 있을 것 같아요ㅎㅎ

코드로 표현하면 아래와 같아요. 정확히 구현되는지는 확인을 안해봐서 흐름만 참고 해주시면 좋을 것 같아요ㅎㅎ

// CardRegisterForm.jsx
const CardRegisterForm = () => {
  // 카드 정보 객체를 state로 관리
  const [cardInfo, setCardInfo] = useState({
    name: "",
    number: ["", "", "", ""],
    expirationDate: "",
    cvc: "",
    passward: "",
  });
  // 저장된 카드 정보를 state로 관리
  const [savedCardInfo, setSavedCardInfo] = useState([]);

  // 사용자가 입력할 때마다 카드 정보 객체 업데이트
  const handleInputEvent = (e) => {
    const { name, value } = e.target;

    setCardInfo((prev) => ({ ...prev, [name]: value }));
  };

  // 완료 클릭 시 Card 컴포넌트에 카드 정보 전달
  const saveCardInfo = (e) => {
    // form 기본 액션 새로고침 방지
    e.preventDefault();

    setSavedCardInfo((prev) => [...prev, cardInfo]);
  };

  return (
    <div>
      <FormTitle>카드 정보를 입력해주세요</FormTitle>
      <form onSubmit={handleSubmit}>
        <DataInput
          name="name"
          InputData={"카드 이름"}
          placeholder={"카드 이름을 입력해주세요"}
          value={cardInfo.name}
          onChange={handleInputEvent}
        />
        <DataInput
          type={"text"}
          name="expirationDate"
          InputData={"유효기간"}
          placeholder={"MM/YY"}
          value={cardInfo.expirationDate}
          onChange={handleInputEvent}
        />
        <DataInput
          name="cvc"
          type={"password"}
          InputData={"CVC"}
          placeholder={"카드 뒷면 3자리 숫자"}
          onChange={handleInputEvent}
          value={cardInfo.cvc}
        />
        <DataInput
          name="password"
          type={"password"}
          InputData={"카드 비밀번호"}
          placeholder={"카드 앞면 2자리 숫자"}
          value={cardInfo.password}
          onChange={handleInputEvent}
        />
        <button type="submit">완료</button>
        <Card savedCardInfo={savedCardInfo} />
      </form>
    </div>
  );
};

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Input에 name이라는 기본 속성이 있는지도 몰랐고, form도 사실 안써봐서 써 볼 생각 자체를 못해봤던거 같네요 기본 속성에는 어떤게 있는지 찾아보면서 익혀보도록 하겠습니다!
확실히 데이터를 저장해야 하는 미션으로 객체로 저장하는게 올바른 방법인 것 같네요 👍🏻

const [cardNumbers, setCardNumbers] = useState(["", "", "", ""]);
const [submittedCardNumbers, setSubmittedCardNumbers] = useState([]);

const handleInputChange = (index) => (e) => {
const newCardNumbers = [...cardNumbers];
newCardNumbers[index] = e.target.value;
setCardNumbers(newCardNumbers);
};

const handleSubmit = () => {
const cardNumber = cardNumbers.join("");
setSubmittedCardNumbers((prev) => [...prev, cardNumber]);
};

return (
<div>
<FormTitle>카드 정보를 입력해주세요</FormTitle>
<DataInput
InputData={"카드 이름"}
placeholder={"카드 이름을 입력해주세요"}
/>
<CardNumberInputs
cardNumbers={cardNumbers}
handleInputChange={handleInputChange}
/>
<DataInput type={"text"} InputData={"유효기간"} placeholder={"MM/YY"} />
<DataInput
type={"password"}
InputData={"CVC"}
placeholder={"카드 뒷면 3자리 숫자"}
/>
<DataInput
type={"password"}
InputData={"카드 비밀번호"}
placeholder={"카드 앞면 2자리 숫자"}
/>
<CompleteButton onClick={handleSubmit}>완료</CompleteButton>
{submittedCardNumbers.map((number, index) => (
<Card key={index} cardNumber={number} />
))}
</div>
);
};

export default CardRegisterForm;

const CompleteButton = styled.button`
margin-top: 0.625rem;
padding: 1.25rem 21.5625rem;
background-color: white;
border-radius: 0px 0px 0.3125rem 0.3125rem;
border: none;
`;

const FormTitle = styled.div`
background-color: #8991a0;
color: white;
font-size: 0.875rem;
padding: 0.9375rem;
border-radius: 0.3125rem 0.3125rem 0px 0px;
margin-top: 3.125rem;
`;
33 changes: 33 additions & 0 deletions src/components/DataInput.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from "react";
import styled from "styled-components";

function DataInput({ type, placeholder, InputData, value, onChange }) {
return (
<InputForm>
<label>{InputData} </label>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

InputDataUpperCamelCase로 구현한 이유가 궁금해요ㅎㅎ
그리고 inputData는 목적이 뚜렷해보이지 않아서 subTitle이나 다른 명시적인 변수명으로 대체해도 좋을 것 같아요ㅎㅎ
마지막으로 CardNumberInputs 에서 사용하는 DataInput의 경우, label이 따로 없기 때문에, inputData가 없으면 label테그를 렌더링하지 않는 조건도 추가되면 좋을 것 같습니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

실수로 InputData만 UpperCamelCase로 작성 한 것 같은데, 다음에는 이런 부분도 유심하게 살펴봐야겠네요! 확실히 subTitle이라는 이름이 더 어울리는 것 같습니다.
카드 넘버 입력 창에서 라벨 없어야 하는데 확실히 해당 변수가 없을 시에는 렌더링 하지 않도록 하는게 자연스럽겠네요!

<input
type={type}
placeholder={placeholder}
value={value}
onChange={onChange}
/>
</InputForm>
);
}

export default DataInput;

const InputForm = styled.div`
display: flex;
flex-direction: column;
padding: 0.625rem;
background-color: white;
border-bottom: 0.0625rem solid #d9d9d9;
input {
border: none;
}

label {
text-align: left;
}
`;
Loading