[STUDY 진행 과정은 깃허브에 계속 commit 하고 있습니다] (많관부!)
https://github.com/just-stopyoon/Seeds-study
3주차 과제 내용
과제 1. App.jsx 코드에서 card 컴포넌트를 추출하고,
children prop으로 서로 다른 내용 전달하기
과제 2. 삼항연산을 사용해서 완료되지 않은 항복의 아이콘에는 X표시 넣기
props 란?
props는 '객체' 이다.
props는 부모 컴포넌트에서 자식 컴포넌트로 값을 전달할 때 사용하는 속성으로, 단방향 데이터 흐름을 갖는다.
따라서, 부모 컴포넌트에서 넘겨 받을 때, 자식 컴포넌트에서는 'props.이름'으로 접근할 수 있다.
props는 읽기 전용 입니다.
함수 컴포넌트나 클래스 컴포넌트 모두 컴포넌트 자체 props를 수정해서는 안된다.
fucntion sum(a, b){
return a + b;
}
이런 함수들을 '순수 함수'라고 호칭한다.
(왜? 입력값을 바꾸려 하지 않고 항상 동일한 입력값에 대해 동일한 결과를 반환하기 때문이다.)
function withdraw(account, amount) {
account.total -= amount;
}
반면 이런 함수들은 자신의 입력값을 변경하기 때문에 순수 함수가 아니다.
React는 매우 유연하지만 한 가지 엄격한 규칙이 있는데,
* 물론 어플리케이션 UI는 동적이며 시간에 따라서 변한다고 한다.
props 특징
1. props는 변경 불가
2. 상호작용이 필요한 경우, state를 사용하여 렌더링 시마다 새로운 props를 받아야 함
props 전달 예시
// utils.js 생성
export function getImageUrl(person, size = 's') {
return `https://i.imgur.com/${person.imageId}${size}.jpg`;
}
// App.jsx
// utils.js에 있는 getImageUrl function을 임포트
import { getImageUrl } from './utils.js';
function Avatar({ person, size }) {
return (
<>
<img
className="avatar"
src={getImageUrl(person)}
alt={person.name}
width={size}
height={size}
/>
<strong>{person.name}</strong>
</>
);
}
export default function Profile() {
return (
<div>
<Avatar
size={100}
person={{
name: 'Katsuko Saruhashi',
imageId: 'YfeOqp2',
}}
/>
<Avatar
size={80}
person={{
name: 'Aklilu Lemma',
imageId: 'OKS67lh',
}}
/>
<Avatar
size={50}
person={{
name: 'Lin Lanying',
imageId: '1bX5QH6',
}}
/>
</div>
);
}
그리고 자식 컴포넌트를 다른 자식 컴포넌트로 전달할 때에는 children props 를 사용하여 전달
// Avatar.jsx
import { getImageUrl } from './utils.js';
// prop 기본값을 지정한 경우입니다 (size = 150)
export default function Avatar({ person, size = 150 }) {
return (
<>
<img
className="avatar"
src={getImageUrl(person)}
alt={person.name}
width={size}
height={size}
/>
</>
);
}
// App.jsx
import Avatar from './Avatar.jsx';
import './App.css';
function Card({ children }) {
return <div className="card">{children}</div>;
}
export default function Profile() {
return (
<div className="profile">
<Card>
<Avatar
size={100}
person={{
name: 'Katsuko Saruhashi',
imageId: 'YfeOqp2',
}}
/>
</Card>
</div>
);
}
조건부 렌더링
조건부 렌더링이란?
React에서는 원하는 동작을 캡슐화하는 컴포넌트를 만들 수 있는데, 이렇게 어플리케이션으 상태에 따라서 컴포넌트 중 몇 개만 렌더링 하는 것을 의미한다.
react에서 조건부 렌더링은 JavaScript에서의 조건 처리와 같이 동작한다.
따라서, if 나 조건부 연산자 와 같은 JavaScript 연산자를 현재 상태를 나타내는 엘리먼트를 만드는 데 사용한다.
아래 두 컴포넌트가 있다고 가정했을 때,
function UserGreeting(props) {
return <h1>Welcome back!</h1>;
}
function GuestGreeting(props) {
return <h1>Please sign up.</h1>;
}
사용자의 로그인 상태에 맞게 위 컴포넌트 중 하나를 보여주는 컴포넌트를 만들고 싶다면?
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
const root = ReactDOM.createRoot(document.getElementById('root'));
// Try changing to isLoggedIn={true}:
root.render(<Greeting isLoggedIn={false} />);
Element 변수
Element를 저장하기 위해 변수를 사용할 수도 있다.
출력의 다른 부분은 변하지 않은 채로 컴포넌트 일부를 조건부로 렌더링 할 수 있다.
로그아웃과 로그인 버튼을 나타내는 두 컴포넌트가 있다고 가정했을 때,
function LoginButton(props) {
return (
<button onClick={props.onClick}>
Login
</button>
);
}
function LogoutButton(props) {
return (
<button onClick={props.onClick}>
Logout
</button>
);
}
class LoginControl extends React.Component {
constructor(props) {
super(props);
this.handleLoginClick = this.handleLoginClick.bind(this);
this.handleLogoutClick = this.handleLogoutClick.bind(this);
this.state = {isLoggedIn: false};
}
handleLoginClick() {
this.setState({isLoggedIn: true});
}
handleLogoutClick() {
this.setState({isLoggedIn: false});
}
render() {
const isLoggedIn = this.state.isLoggedIn;
let button;
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{button}
</div>
);
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<LoginControl />);
이 컴포넌트는 현재 상태에 맞게 <LoginButtion /> 이나 <LogoutButtion /> 을 렌더링하고,
<Greeting /> 도 함께 렌더링한다.
논리 && 연산자로 If 를 인라인으로 표현하기
JSX 안에서는 중괄호를 이용해서 표현식을 포함할 수 있다.
그 안에 논리 연산자 && 를 사용하면 쉽게 Element를 조건부로 넣을 수 있다.
{unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}
논리 연산자 &&란?
두 피연산자가 모두 있으면 반환 true 하고, 그렇지 않으면 반환 false 합니다.
피연산자는 평가 전에 암시적으로 형식 bool 으로 변환되고 결과는 형식 bool입니다.
논리 AND에는 왼쪽에서 오른쪽으로의 결합성이 있습니다.
조건부 연산자로 If-Else 구문 인라인으로 표현하기
| 조건부 연산자의 기본 틀 |
condition ? true : false 를 사용한다.
예시
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in.
</div>
);
}
가독성이 살짝 떨어지는 느낌이지만,,,;;
조건이 너무 복잡하다면 컴포넌트를 분리하기 좋을 때일 수도 있다는 것을 기억하라고 한다! (왜지?)
삼항 연산자는 주로 양자택일의 경우에 사용한다.
그냥 한 개만 보여주고 나머지는 알바노? 면 단축 평가를 사용한다.
( 단! 동적클래스를 추가해야 할 경우에는 && 단축 평가 사용 시 false가 찍히므로 삼항연산자를 사용해야 함 )
컴포넌트가 렌더링하는 것을 막기
가끔 다른 컴포넌트에 의해 렌더링 될 때 컴포넌트 자체를 숨기고 싶을 때가 있다면?
→ 렌더링 결과를 출력하는 대신 null 을 반환하면 해결할 수 있다.
아래 예시에서는 <WarningBanner /> 가 warn prop의 값에 의해서 렌더링 된다.
만약 prop이 false라면 컴포넌트는 렌더링하지 않는다.
function WarningBanner(props) {
if (!props.warn) {
return null;
}
return (
<div className="warning">
Warning!
</div>
);
}
컴포넌트의 render method로부터 null을 반환하는 것은 생명주기 method 호출에 영향을 주지 않는다.
3주차 과제
과제 1. App.jsx 코드에서 card 컴포넌트를 추출하고, children prop으로 서로 다른 내용 전달하기
과제 2. 삼항연산을 사용해서 완료되지 않은 항복의 아이콘에는 X표시 넣기
- [조건]
- Item 컴포넌트 함수를 작성하고 name, is Packed를 props 구조분해로 전달받기
- isPacked의 값이 true면 장비명과 체크아이콘을 보여주고 false면 장비명과 X아이콘 표시
- isPacked값이 true면 .checked 클래스 걸어주기 (밑에 App.css있습니다)
과제 1)
사실 이 과제가 잘 이해가 안됐다... (이렇게 하는게 맞는 건가..?)
1. 일단 그동안 너무 난잡했던 App.js 를 한 번 싹 정리했다.

2주차 과제였던, Header / Footer 는 frame 폴더 안에 넣어서 따로 선언했다.

그리고 따로 import 해줬다! 항상 꾸역꾸역 .js 까지 붙였었는데 안 붙여도 된대서 바로 뗐다 ㅎㅎ
function App() {
return (
<div className="App-back">
<Header />
<Profile/>
<Section/>
<Footer />
</div>
);
}
아주 깔끔해진 App ! 맘에 든다.
사실 이번 과제는 이런 게 아니다 !!!! 빨리 다시 한 번 과제를 읽어보자면,,,
App.jsx에서 card 컴포넌트를 추출하고, children prop으로 서로 다른 내용 전달하기
근데 나 이게 진짜 뭔지 모르겠다...
import React, { useState } from "react";
import "./Profile.css";
function Profile(){
return (
<div className="profile">
<div className="card">
<h3 clssName = "post-box-title">Photo</h3>
<img
className="avatar"
src="https://i.namu.wiki/i/QIkTRQptsQPv81v4nCBvhwCY5hvwpMnOvQF1qHbW2pkht6RMGyp1Vc0GAstqDlA-mFSVCWbnXDt_l3btHKr_CA.gif"
alt="WonWoo"
width={100}
height={100}
/>
</div>
<div className="card">
<h3 className = "profile-title">About</h3>
<p className = "profile-text">
세븐틴의 가장 낮은 목소리 원우입니다.
</p>
</div>
</div>
);
}
function Card({ children }) {
return (
<div className="card">
{children}
</div>
);
}
export default Profile;
그래서 그냥 Profile을 따로 선언해서 card 컴포넌트를 불러오고,, children prop 으로 요소를 바꿔주기...
(이거 아닐 수도 있을 것 같다)
얼레벌레 과제 1을 끝내고...
과제 2)
function Section() {
return (
<div className = "set-middle">
<p className = "post-box-title">Section</p>
<div className = "square">
<ul className = "post-box">
<Item isPacked={true} name="우주복" />
<Item isPacked={true} name="황금잎이 달린 헷멧" />
<Item isPacked={false} name="가족사진" />
</ul>
</div>
</div>
);
}
section 함수를 수정해주었고!
function Item({ name, isPacked }) {
return (
<li className={`item ${isPacked ? 'checked' : ''}`}>
{isPacked ? name + ' ✔️' : name + ' ❌'}
</li>
);
}
item 컴포넌트 함수를 선언해서 name, is Paced를 구조 분해로 전달 받았다.
그리고 삼항 연산자를 사용해서 isPacked 값이 true 면 name과 체크아이콘, 그리고 checked 클래스 걸어주기!
결과물

약간의 덕심을 추가해봤다 ㅎㅎ 과제 완료!
'Front-end > React 리액트' 카테고리의 다른 글
| [Seeds-study] (React/리액트) 5주차 React Hooks (useState) (1) | 2024.03.24 |
|---|---|
| [Seeds-study] (React/리액트) 4주차 이벤트 처리와 폼 (1) | 2024.03.18 |
| [Seeds-study] (React/리액트) 2주차 JSX와 컴포넌트 기초 (0) | 2024.03.03 |
| [Seeds-study] (React/리액트) 1주차 React 개발 환경 설정 (2) | 2024.02.25 |
| [React 리액트/CSS] CSS가 제대로 적용되지 않을 때 해결법 (2) | 2024.02.20 |
