OSSP 이클래스

찜와와·2023년 7월 14일

Frontend

목록 보기
2/2

구현해야 하는 기능: 강의실페이지, 과제페이지(등록), 공지사항, 쪽지

Q&A 페이지

스타일 컴포넌트

1) CSS in JS?

HTML, CSS, JavaScript 3개로 분리하는 것이 아니라 여러개의 컴포넌트로 분리하고 각 컴포넌트에 HTML, CSS, JavaScript를 모두 넣은 패턴을 사용한다.

2) 패키지 설치

styled component는 npm 커맨드로 간단히 설치할 수 있다.

npm i styled-components

3) 기본 문법

  • styled-components 패키지에서 styled 함수 import
  • styled는 HTML 이나 react 컴포넌트에 원하는 스타일 적용할 때 사용
  • vh: view-height rem: html 요소 크기의 몇배인지로 크기를 정함 (html 요소 크기 기본값은 16px의 글자크기)

4) 고정 스타일링

<button> HTML 요소에 원하는 스타일을 적용한 후 StyledButton변수에 저장

5) 가변 스타일링 1

styled component 는 react 컴포넌트에 넘어온 props에 따라 다른 스타일을 적용하는 기능을 제공

error issue 1) module not found Error

원인: CRA에서 컴파일은 src 내부에서만 일어나는데 이때 js에서 import된 이미지와 같이 엮여 있는 파일들이 모두 컴파일 대상이다. 이미지를 js 파일에 import하는 경우나 css 파일에서 background image로 사용하는 경우라면 "이미지가 src 폴더에 존재해야 한다".

6) select-box 추가

대신 백엔드에 로그인값을 넘기면 수강중인 강의들이 id로 받아옴
걔네가 select-box의 옵션들이 됨
react셀렉트
1. 셀렉트 박스를 만들기 위해 react-select 설치

npm i react-select
  1. value를 react-select에 반영 (우선은 더미데이터를 넣어둠)
import Select from "react-select";

let selectOptions = [
	{value: "use", label: "사용"},
    {value: "unused", label: "미사용"}
 ];
 
export const Example = () => {
 	const [selectedValue, setSelectedValue] = useState("use");
    
    return(
    	<Select
        	className="selectItem"
            onChange={(e) => setSelectedValue(e.value)}
            options={selectOptions}
            placeholder="유형선택"
            value={selectOptions.filter(function (option) {
            	return option.value === selectedValue;
            })}
        />
    )
}

export default Example;

또는

const OPTIONS = [
	{ value: "apple", name: "사과" },
    { value: "banana", name: "바나나" },
    { value: "orange", name: "오렌지" }
]

const SelectBox = (props) => {
	return (
    	<select>
        	{props.options.map((option) => (
            	<option
                	value={option.value}
                    defaultValue={props.defaultValue === option.value}
                >
                	{option.name}
                </option>
            ))}
        </select>
    );
};

function App() {
	return <selectBox options={OPTIONS} defaultValue="bananana"></SelectBox>;
}

export default App;
  1. 더미데이터를 한 페이지에 구현

    select box를 button 컴포넌트처럼 onCHange event 를 통해 선택된 값에 접근
    선택된 값은 e.target.value 안에 포함되어 있어 선택할 때마다 선택된 옵션의 value값이 찍힌다.

7) 공지사항 바 구현

기능: 더미데이터에 공지사항 내용들을 저장하고 가져옴 + 누르면 모달창으로 띄움
1. 더미데이터 만들기

data.json

{
	"days" : [
    	{"id" : 1, "day" : 1},
        {"id" : 2, "day" : 2},
        {"id" : 3, "day" : 3}
    ],
    "words" : [
    	{
        	"id" : 1,
            "day" : 1,
            "eng" : "book",
            "kor" : "책",
            "isDone" : false
        },
        {
        	"id" : 2,
            "day" : 1, 
            "eng" : "apple,
            "kor" : "사과",
            "isDone" : false
        },
        {
        	"id" : 3,
            "day" : 2,
            "eng" : "car",
            "kor" : "자동차",
            "isDone" : false
        }
    ]
}

Daylist.js

map 함수 정의
map(): 배열을 받아와 새로운 배열로 반환해준다.

import dummy from "../db/data.json";

export default finction daylist(){
	console.log(dummy);
    return(
    	<ul className="list_day"(
        	{dummy.days.map(day => (
				<li key={day.id}> Day {day.day} </li>
        ))}
        </ul>
    );
}

Day.js

import dummy from "../db/data.json";

export default function Day(){
	return(
    	<table>
        	<tbody>
            	{dummy.words.map(word => (
                	<tr}>
                    	<td>{word.eng}</td>
                        <td>{word.kor}</td>
                    </tr>
                ))}
            </tbody>
        </table>
    );
}

App.js

위에서 구현한 두 함수를 모두 사용함
function App() {
return(

	<div className="App">
); }

noti-더미데이터 적용

1) 목록태그 (기본)
<ol> :ordered list, 순서가 있는 목록을 만드는데 사용
<ul> :unordered list, 순서가 필요 없는 목록을 만드는데 사용
<dl> :definitin list, 사전처럼 용어를 설명하는 목록을 만드는데 사용
예시)

<ul>
	<li>명사</li>
    <li>형용사</li>
    <li>동사</li>
    <li>부사</li>
</ul>

error issue 2) map() 구현중 발생된 'undefined'

상황: 위의 예시들로 console.log() 까지 찍었을때 결과가 모두 발견, 그러나 html에 나타나지 않음
원인: react는 렌더링이 화면에 커밋 된 후에야 모든 효과를 실행하기 때문이다
(return에서 첫 턴에 데이터가 아직 안들어와도 렌더링이 실행되어 그 데이터가 undefined로 정의됨)

📌문제해결법
1. && 를 사용한다
(javascript에서 true&&expression 은 항상 expression으로 실행되고 false&&expression은 항상 false로 실행된다. 따라서 조건이 참이면 && 바로 뒤의 요소가 출력에 나타난다. 거짓이면 react는 무시하고 건너뛴다.)

8) select box에서 가져온 value 값으로 컴포넌트 전환

onChange value for component change

예시

<select onChange={onClickCoinsOptionHandler} value={selectedCoin}>
	{coins.map((coin) => {
    	return (
        	<option key={coin.id} value={coin}>
            	{coin.name} ({coin.symbol})
            </option>
        );
    })}
</select>

coins: 비트코인객체 리스트 (더미데이터)
selectedCoin: 선택된 코인객체

9) onClick으로 보여주는 컴포넌트 전환

이때 컴포넌트는 value 독립적임
(라우터: URL에 따라 다른 컴포넌트를 보여줄 때
vs useState + 삼항연산자 : 동일 URL, 페이지 내에서 보여주는 컴포넌트를 전환할때)

기본 설정

import React, {useState} from "react";

접근 방법 (상황설명)

1) calendar를 보여주는 state 값을 boolean으로 설정 (기본값: true)
2) timeline 버튼 onClick 시 calendar 값을 false, calendar 클릭시 다시 true
3) 삼항연산자를 이용해 해당 값이 true일 때 calendar, false 일때 timeline

코드 예시

function Main(){
	const [viewCalendar, setViewCalendar] = useState(true);
   
   return(
   	<div className="contentWrapper">
       	<div className="contentTitle">
           	<Button
               	onClick={() => setViewCalendar(ture)}
               >
               달력
               </Button>
               <Button
               	onClick={() => setViewCalendar(false)}
               > 
               타임라인
               </Button>
               </div>
               <div className="mainComponentWrapper">
               	{viewCalendar ? <Calendar/> : <Timeline/>}
               </div>
           </div>
       </div>
   );
}

export defualt Main;

tip : 클래스형 컴포넌트 vs 함수형 컴포넌트

비교예제
props: 컴포넌트의 속성을 설정할 때 사용하는 요소
(모든 리액트 컴포넌트는 자신의 props를 다룰 때 순수 함수처럼 동작해야하고 수정되는 것은 state만 수정해야함!!)

1) 클래스형 컴포넌트

  • class 키워드가 필요함

  • component를 상속받아야함

  • 화면에 표시하기 위해 render() 메서드가 필요함

  • constructor 안에서 this.state를 통해 초기 갑 설정이 가능 (constructor없이도 설정가능)
    State차이

  • state는 객체형식으로 존재

  • this.setState 함수로 state의 값을 변경할 수 있음
    Props차이

  • this.props로 불러옴

    2) 함수형 컴포넌트

  • 클래스형과 비교하여 훨씬 간결한 코드를 작성할 수 있음

  • 함수 자체가 렌더함수이므로 render() 메서드를 사용하지 않아도 됨
    State차이

  • component를 상속받지 않아도 됨

  • useState로 state 핸들링 가능 (state, setState: state변경해주는 함수)
    Props차이

  • 렌더함수의 parameter로 props를 전달받아 사용함

3) 코드비교
클래스형

constructor(props){
	super(props);
    
    this.state = {
    	name: "soopiri",
        items: []
    };
}

또는

onClick ={() => {
	this.setState({ price: price + 100 });
}}

함수형

const App = () => {
	const [name, setName] = useState("soopiri");
    
    const onButtonClick = {() => {
    	setName("HAHA");
    }}
}

JSON.stringify

참고자료
JSONstringify 사용법
JSON: 자바스크립트에서 사용할 목적으로 만들어진 포맷
데이터 교환을 목적으로 사용하는 경우가 많다

JSON.stringify : 객체를 JSON으로 변환
JSON.parse : JSON을 객체로 변환

let student = {
	name: 'JOHN',
    age: 30,
    isAdmin: false,
    courses: ['html', 'css', 'js'],
    wife: null
}

let json = JSON.stringify(student);
console.log(typeof json);
console.log(json);
//JSON으로 인코딩된 객체

JSON.stringify(student)를 호촐하니 student가 문자열로 변함
객체, 배열, 원시형(문자형, 숫자형, 불린형 값, null) 에서 모두 사용 가능
그러나 객체 프로퍼티(함수형 메서드, 심볼형 메서드, undefined 프로퍼티)는 처리할 수 없음
함수형 프로퍼티인 경우의 예시)

let user = {
	sayHi(){
    	alert('hello');
    },
    [Symbol('id')] :123,
    something: undefined
};
console.log(JSON.stringify(user);

위 경우 빈 객체가 출력된다.

중첩 객체도 알아서 문자열로 변환해줌
예시)

let meetup = {
	title: "Conference",
    room: {
    	numver: 23,
        participants: ["john", "ann"]
    }
}

alwer(JSON.stringify(meetup));

replacer로 원하는 프로퍼티만 직렬화할 수 있다.

let json = JSON.stringify(value[, replacer,space])

value: 인코딩하려는 값
replacer: JSON으로 인코딩하길 원하는 프로퍼티가 담긴 배열
space: 서식변경을 목적으로 사용하는 공백문자 수

버튼 클릭으로 원하는 컴포넌트 렌더링하기

참고자료
구현화면은 다음과 같음

로직
-버튼 클릭
-버튼 클릭한 버튼의 상태값 저장
-상태값에 따른 컴포넌트 렌더링

1. state 선언, onClick이벤트 선언

클릭한 버튼의 name값을 state에 저장한다
value가 될 수 있고 innerText 등 상황에 맞게 사용한다
예시)

const [content, setContent] = useState();

const handleClickButton = e => {
	const {name} = e.target;
    setContent(name);
}

2. 객체 셍성

객체의 key를 버튼의 name값과 동일하게, 값은 렌더링할 컴포넌트로 한다.
예시)

const selectComponent = {
	first: <First />,
    second: <Secont />,
    third: <Third />,
    fourth: <Fourth />,
    fifth: <Fifth />
};

3. 버튼에 onClick 이벤트 할당

반복되는 레이아웃은 map함수를 이용하여 코드를 줄일 수 있다.
예시

<Container>
	{MAIN_DATA.map(data => {
    return(
    	<Button>
        	onClick={handleClickButton}
            name={data.name}
            key={data.id}
            {data.text}
        <Button />
    );
    })}
<Container />

4. 옵셔널 체이닝, 객체의 key 변수 처리

state 값 뒤에 && 값을 적고 div로 객체와 key를 [state] 감싸줍니다.

{content && <div> {selectComponent[content]}</div>}

content가 true라면 <Content>를 렌더링하겠다.
현재 content는 state값으로 초기값은 () qlsrkqtdmfh ㄹ민ㄷ gks rkqtdmfh qls ghkausdl skdhsek.

의사결정

라우터를 사용하지 않고 컴포넌트 전환을 사용한 이유:
selectbox를 토대로 변한값을 가져오고 싶어서

slice() vs splice()함수

1) splice() 함수
1번 요소 : 시작 인덱스
2번 요소 : 몇 개의 값을 삭제할 지
3번 요소 : 추가할 값을 가변인자로 넘김
반환값 : 삭제된 값을 담은 배열

react-table 라이브러리

bootstrap대신 react-table 라이브러리르 사용하는 이유
1) bootstrap을 사용하는 경우 스타일링으로 사용하고자 한 styled-components를 포기해야 함 = bootstrapsass기반으로 움직이므로 styled-component로 구현한 내용을 모두 sass로 변경해야 했는데 전체 스타일링을 변경해야 한다.
2) react-table의 경우 코드가 더 깔끔해졌다.
= bootstrap을 사용하는 경우에도 thead, th, td, tr 이 빠짐없이 사용되기에 셀의 양이 증가할 수록 코드가 복자해졌다. 그러나 reacttable의 경우 mapping을 활용하기에 최적화되어 코드가 더욱 편리해졌다.

사용방법

react-table 설치

npm install react-table --save
yarn add react-table

테이블 데이터 설정하기

테이블에 입력할 data를 알맞게 정리해야 한다.
data에는 각 세로 열의 header 데이터를 담은 columns와 header와 연결되어 전체 셀 데이터를 담은 data가 담겨져 있다.

모든 데이터는 처음 한번만 render되므로 데이터 호출시 useMemo를 사용한다.

column 입력

column은 배열 형태로 입력되어야 한다.
배열 안에는 각 column을 구성할 항목들이 object 형태로 들어가있다.
header에는 테이블에 출력될 header 이름을 ,accesssor에는 data object와 연결할 key name을 기재한다

data 입력

data도 동일하게 배열 내 객체 형태로 입력되어야 한다.
columns의 accessor 와 key name이 일치한지 주의하여 입력한다.
객체 내 object 형태로만 넘겨주면 되기에 네트워크로 받아온 데이터를 바로 넘길 수 있다.

const [info, setInfo] = useState();

const getTamWallet = () => {
	data.getTamWallet().then(item => setInfo(item));
};

const data = useMemo(() => infom [info])

table 구성하기

columns 와 data가 준비되었다면 아래와 같이 table을 출력한다
table을 출력하려면 기본적으로 아래 5가지가 필요하다
1) getTableProps() : react-table로 table을 만들려면 선언이 필요하다 (property와 method를 활용할 수 있다)
2) getTableBodyProps() : react-table로 table body를 만들어야되는 경우 선언되어야 한다.
3) headerGroups : header부분에 들어갈 데이터를 담고 있다.
4) rows : 전달한 data를 받는 곳이다
5) prepareRow() : 각각의 data들을 한 줄씩 묶음으로 가공한다.

import React from "react";
import {useTable} from 'react-table';
import styled from 'styled-components';

const Table = ({columns, data}) => {
	const {getTableProps, getTableBodyProps, heaerGroups, rows, prepareRow} = useTable({columns, data});
    
    return (
    	<TableSheet {...getTableProps()}>
        	<TableHeader>
        		{headerGroups.map(header => (
            	//getHeadeGroupProps를 통해 header 배열을 호출한다
            		<Header {...header.getHeaderGroupProps()}>
                		{header,headers,map(col => (
                    	//getHeaderProps는 각 셀 순서에 맞게 header를 호출한다
                    		<Th {...col.getHeaderProps()}> {col.reander('Header')}</Th>))}
                	</Header>
                 ))}
            </TableHeader/>
            <tbody {...getTableBodyProps()}>
            	{rows.map(row => {
                	prepareRow(row);
                    return (
                    //getRowProps는 각 row data를 호출한다.
                    	<tr {...row.getRowProps()}>
                        	{row.cells.map(cell => (
                            	<Td {...cell.getCellProps()> {cell.render('Cell')</Td>
                            ))}
                        </tr>
                      );
                    })}
            </tbody>               
        </TableSheet>
    );
}

0개의 댓글