working_helen

[React 일기장 프로젝트] UI 구성하기 본문

외부 수업/React 스터디

[React 일기장 프로젝트] UI 구성하기

HaeWon_Seo 2024. 1. 15. 23:10

강의명 : 한입 크기로 잘라 먹는 리액트(React.js) - 섹션 5. React 기본 - 간단한 일기장 프로젝트

 

일기장 입력 + 저장 기본 구조 구축하기

1. 일기장 입력 컴포넌트 : DiaryEditor.js

2. 일기장 목록 컴포넌트 : DiaryList.js

3. 일기장 목록 아이템 컴포넌트 : DiaryItem.js

4. 전체 애플리케이션 컴포넌트 : App.js

 


일기장 컴포넌트 구조

 

 

1. 일기장 입력 컴포넌트 : DiaryEditor.js

1) [state, setState] - State 변수 생성

- 사용자 입력을 받아 값이 변화할 변수들 State 객체 생성

- state = { author, content, emotion'}

 

2) handleChangeState -  State 변수 변경 함수

- handleChangeState : event e에 대하여 State 객체 내 하나의 State 변수 값을 변경 

 

 

3) Reference 객체 생성

- Reference 객체 = 특정 컴포넌트를 가르키는 변수 

- Reference객체.current = 현재 가르키는 컴포넌트

const ReferenceVariable = useRef();

 

4) handleSubmit - 일기장을 저장 + 잘못된 입력 시 focus와 경고창 

(MDN) HTMLElement.focus() 메서드는 지정된 요소에 초점을 맞출 수 있는 경우 해당 요소에 초점을 설정

 

- HTML요소.focus() 

- focus를 줄 객체가 무엇인지 가르키기 위해 Refernce 객체와 함께 사용

// Reference 변수가 현재 가르키고 있는 요소를 focus 한다
ReferenceVariable.current.focus()

 

 

5) DiaryEditor 리턴 입력창

- reference 변수가 가르키는 요소에, name과 value로 하는 handelChangeState 함수 수행

- 사용자 입력이 입력창에 그대로 나타나도록 State 변화를 구현한 것 

 

- <input> : 사용자 인터페이스 상자를 만드는 태그, 입력 결과를 State 변수로 받음

 ==> 사용자 입력 받기 : <input> 태그 + State 변수

 

- <textarea> : 사용자가 여러 줄을 자유형식으로 작성할 수 있는 텍스트 편집 컨트롤러 

  <select> : 옵션을 제공 + 선택 기능의 컨트롤러, <option value={}> 태그와 함께 사용 

  <button> : 클릭으로 사용자의 입력을 받는 컨트롤러

 

 

[ Diary Editor 코드 ]

import { useRef, useState } from "react";

const DiaryEditor = () => {

    //State 변수 생성 : 실행에 따라 값이 변하는 변수들 처리
    const [state, setState] = useState({
        // State 초기값 지정, 객체 형태
        author:"",
        content:"",
        emotion: 5,
    });


    //Reference 객체 생성
    const authorInput = useRef();
    const contentInput = useRef();


    //event e에 대해 하나의 state만 변화
    const handleChangeState = (e) => {
        setState({
            // 전체 State 객체를 Spread
            // 필요한 key만 value 변경
            ...state,
            [e.target.name]: e.target.value,
        });
    };


    const handleSubmit = () => {
        
        // 잘못된 입력에 focus 주기
        if(state.author.length<1){
            authorInput.current.focus();
            return;
        }

        if(state.content.length<5){
            alert("본문을 5글자 이상 적어주세요.");
            contentInput.current.focus();
            return;
        }

        alert("저장 성공");
    }


    // DiaryEditor의 리턴
    return (
        <div className="DiaryEditor">
            <h2>오늘의 일기</h2>
            <div>
                <input 
                ref = {authorInput}     //reference 대상 지정
                name = "author"         //변수명
                value={state.author}    //변수값
                onChange={handleChangeState}
                />
            </div>

            <div>
                <textarea
                ref = {contentInput}
                name = "content"
                value={state.content}
                onChange={handleChangeState}
                />                
            </div>

            <div>
                오늘의 감정 점수 : 
                <select
                name="emotion"
                value={state.emotion}
                onChange={handleChangeState}>
                    {/* select 값 후보들 */}
                    <option value={1}>1</option>
                    <option value={2}>2</option>
                    <option value={3}>3</option>
                    <option value={4}>4</option>
                    <option value={5}>5</option>
                </select>
            </div>

            <div>
                <button onClick={handleSubmit}>일기 저장하기</button>
            </div>

        </div>
    )
};


export default DiaryEditor;

 

DiaryEditor UI 화면

 

 

 

 

2. 일기장 목록 컴포넌트 : DiaryList.js

1) diaryList props 전달

- 일기장 배열 변수 diaryList 1개가 전달

- props 객체 내 원소가 하나일때, 해당 원소에 바로 접근 가능

  (props.diaryList로 받지 않고 바로 diaryList 변수명 사용)

 

2) DiaryItem으로 props 전달

- diaryList에 있는 각 원소를 DiaryItem의 props로 전달

- 배열.map((원소) => (함수)) : 배열의 각 원소마다 함수를 적용한 결과 새로운 배열 반환

- key={item.id} : "Each child in a list should have a unique "key" prop." 에러를 해결, list의 각 원소가 구분될 수 있도록 각 원소마다 고유하게 가지고 있는 key값을 props의 key로 지정

- 배열의 각 원소마다 DiaryItem의 html 태그가 리턴

 

 

[ DiaryList.js 코드 ] 

import DiaryItem from './DiaryItem.js' 

// App으로 부터 diaryList 배열을 props로 전달받음
const DiaryList = ({diaryList}) =>{

    // DiaryList의 리턴
    return (
    <div className = 'DiaryList'>
        <h2>일기 리스트</h2>
        <h4>{diaryList.length}개의 일기가 있습니다.</h4>

        <div>
            // DiaryItem으로 props 전달
            {diaryList.map((item)=>(
                <DiaryItem key={item.id} {...item}/>
                // html 태그 리턴
            ))}
        </div>

    </div>
    );
};

// defualt Props 정의
DiaryList.defaultProps = {
    diaryList: [],
}

export default DiaryList;

 

 

 

 

 

3. 일기장 목록 아이템 컴포넌트 : DiaryItem.js

1) DiaryList의 props 전달

- DiaryList로부터 일기장 배열의 각 원소마다 {author, content, created_date, emotion, id} props 전달

 

2) DiaryItem의 리턴

- 각 일기장마다 작성자, 날짜, 감정 점수, 내용 정보 세트를 리턴

- <span> : 줄바꿈 되지 않는 인라인 요소

- 일반 정보는 'info' class로, 내용은 'content' class로 구분

- Date객체.toLocaleDateString() : 사람들이 사용하는 형식으로 날짜 표현 반환 

 

 

[ DiaryItem.js 코드 ]

// props = {author, content, created_date, emotion, id}
const DiaryItem = (props) => {

    // DiaryItem 리턴
    return (
        <div className = 'DiaryItem'>
            <div className="info">
                <span>작성자 : {props.author}</span>
                <span className="date"> | {new Date(props.created_date).toLocaleDateString()}</span>
                <div>감정 점수: {props.emotion}</div>
            </div>

            <div className="content">
                <div>{props.content}</div>
            </div>
        </div>
    );
};


export default DiaryItem;

 

 

 

 

4. 전체 애플리케이션 컴포넌트 : App.js

- DiaryEditor와 DiaryList 컴포넌트를 import

- App.css 파일로 style 지정

 

- Date().getTime() : 현재 시간을 받아서 ms 단위로 반환

- 일기장 배열을  DiaryList에 props로 전달

- App의 리턴 : DiaryEditor와 DiaryList의 html 코드를 리턴

 

 

[ App.js 코드]

import './App.css';
import DiaryEditor from './DiaryEditor';
import DiaryList from './DiaryList';

// 예시를 위한 일기장 배열
const dummyList = [
  {id:1,
  author: "haewon",
  content:"Hello",
  emotion: 5,
  created_date: new Date().getTime(),
  },
  {id:2,
    author: "Mark",
    content:"Hi",
    emotion: 2,
    created_date: new Date().getTime(),
    }
];


function App() {
  return (
    // App.css 적용을 위해 className을 'App'으로 지정
    <div className="App">
      <DiaryEditor />
      {/* html 태그 리턴 */}
      <DiaryList diaryList={dummyList}/>
      {/* html 태그 리턴 */}
    </div>
  );
}

export default App;