본문 바로가기

회고록(TIL&WIL)

TIL 2023.03.16 React (기본문법 jsx, useState, react-router)

React

프로젝트 생성

npx create-react-app react02

JSX

html 작성하듯이 작성하여 그대로 return 가능하도록한 문법 기존의 경우는 createElement를 해줘야하나 그러지 않아도 가능
단, 루트 element가 반드시 필요하기 때문에 p 태그같은것은 혼자 쓸 수 없다. 반드시 div 태그 든 <>(fragment)가 있어야만 한다.
태그는 반드시 닫아 주어야만 한다.

import "./App.css"; => .cl01{background-color: red};

function Ex01() {
    // 자바스크립트의 변수를 담아 사용할 때는 {}를 이용하면 된다.
    // let msg = 'Ex01 page';
    // let color = {color: "red"};
    // return <h1 style={color}>{msg} function을 이용한 엘리먼트</h1>
  
    // root element가 반드시 존재해야한다.
    // return <>
    //    <h1>제목</h1>
    //    <p>내용</p>
    // </>
    
    // className은 class를 준 것과 똑같이 사용된다.
    return <>
        <h1 className="cl01">제목</h1>
        <p>내용</p>
    </>
}

export default Ex01;

Component

컴포넌트를 생성할 때 반드시 첫글자를 대문자로 작성해야 한다. 그래야 기존의 태그보다 우선으로 인식된다.
클래스방식은 현재는 사용되지 않는다. 참고만.

props를 이용해서 값을 주고 받을 수 있다.

class H2 extends React.Component {
    constructor(props){
        super(props);
        this.msg = props.msg2;
    }
    render() {
        return <h1>H2 : {this.msg}</h1>
    }
}
function Ex02(props) {
    return (
    <>
        <h1 className="cl01">제목</h1>
        <p>{props.msg}</p>
    </>
    )
}
function Ex01() {
    return (
        <>
            <Ex02 msg="환영합니다.ddd"/>
            <H2 msg2="클래스 방식"/>
        </>
    ) 
}
function Ex02({sub, msg}) {
    return (
    <>
        <h1 className="cl01">{sub}</h1>
        <p>{msg}</p>
    </>
    )
}
function Ex01() {
    return (
        <>
            <Ex02 sub="한글" msg="환영합니다.ddd"/>
            <Ex02 sub="eng" msg="wellcome"/>
            <H2 msg2="클래스 방식"/>
        </>
    ) 
}

state

값만 바꾼다고해서 해당 DOM을 재수정 하진않기 때문에 이를 지정해주기 위해서 state를 사용한다.
기존의 msg의 값을 바꿔도 바로 적용되지않으며 반드시 setState를 해야만 DOM을 갱신한다.

class H2 extends React.Component {
    constructor(props){
        super(props);
        this.msg = props.msg2;
        this.state = {brand: "ford"};
    }
    render() {
        return (
        <>
            <h1>H2 : {this.state.brand} {this.msg}</h1>
            <button onClick={()=>{
                this.msg = '바뀜';
                const obj = this.state;
                this.setState({...obj, brand: "수정"});
            }}>입력</button>
        </>
        )
    }
}

event

const Ex03 = ()=>{
    const func01 = () => {
        alert('클릭2');
    };
    return (
        <div>
        <button onClick={()=>{
            alert('클릭');
        }}>버튼</button>
        <button onClick={func01}>클릭2</button>
        <button onClick={func01}>클릭3</button>
        </div>
    )
};

if

function Goal(props) {
  const isGoal = props.isGoal;
  if (isGoal) {
    return <MadeGoal/>;
  }
  return <MissedGoal/>;
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Goal isGoal={false} />);

&&

function Garage(props) {
  const cars = props.cars;
  return (
    <>
      <h1>Garage</h1>
      {cars.length > 0 &&
        <h2>
          You have {cars.length} cars in your garage.
        </h2>
      }
      
      {cars.length > 0 ? (
          <h2>
            You have {cars.length} cars in your garage.
          </h2> )
          : null
        }
    </>
  );
}

const cars = ['Ford', 'BMW', 'Audi'];
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Garage cars={cars} />);

list - map

function Ex01() {
    const cars = ['Ford', 'BMW', 'Audi'];
    return (
        <>
            <ul>
                { 
                cars.map((ele, idx)=><li key={idx}>{ele}</li>)
                }
            </ul>
        </>
    ) 
}

rfc - react functional component

새로운 컴포넌트를 만들 때 rfc 만 입력하고 자동완성하면 틀을 생성해준다.

import React from 'react'

export default function Ex02() {
  return (
    <div>Ex02</div>
  )
}

useState

import React, { useState } from "react";

export default function Ex02() {
    const [title, setTitle] = useState('제목없음');
    const [ipval, setIpval] = useState(["",'']);

    const func01 = (e) => {
        e.preventDefault();
        setTitle(e.target.ename.value);
    }
    const func02= (e) => {
        if(e.target.name=='ename'){
            setIpval([e.target.value, ipval[1]]);
        } else if(e.target.name=='ename2'){
            // setIpval([ipval[0], e.target.value]);
            setIpval(ipval.map((ele,idx)=> idx==1 ? e.target.value : ele));
        }
    }
    return (
        <>
        <div>
            <h1>{title}</h1>
            <input/>
            <button onClick={() => {setTitle('제목있음')}}>클릭</button>
        </div>

        <form onSubmit={func01}>
            <h1>{title}</h1>
            <input name="ename" onChange={func02} value={ipval[0]}/>
            <input name="ename2" onChange={func02} value={ipval[1]}/>
            <button>전송</button>
        </form>
        </>
    )
}

React-Router

npm react-router-dom
npm start

index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import Ex01 from './Ex01';
import Ex02 from './Ex02';
import Ex03 from './Ex03';
import Nav from './Nav';
import reportWebVitals from './reportWebVitals';
import { Route, Routes, BrowserRouter} from 'react-router-dom';

// class My01 extends React.Component {
//   render() {
//     return <h1>환영합니다</h1>
//   }
// }

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <BrowserRouter>
    <Routes>
      <Route path="/" element={<Nav />}>
        <Route index element={<App />}/>
        <Route path="p1" element={<Ex01 />}/>
        <Route path="p2" element={<Ex02 />}/>
        <Route path="p3" element={<Ex03 />}/>
      </Route>
    </Routes>
  </BrowserRouter>
);

reportWebVitals();

Nav.js - Link 태그를 이용하면 SPA 처럼 됨

import React from 'react'
import { Link, Outlet } from 'react-router-dom'

export default function Nav() {
  return (
    <div>
        <nav>
            <Link to="/">home</Link>
            <Link to="/p1">p1</Link>
            <Link to="/p2">p2</Link>
            <Link to="/p3">p3</Link>
        </nav>
    <Outlet/>
    </div>
  )
}

CRUD

Front - React

index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import DeptList from './pages/DeptList'
import DeptAdd from './pages/DeptAdd'
import DeptDetail from './pages/DeptDetail'
import Nav from './Nav';
import reportWebVitals from './reportWebVitals';
import { Route, Routes, BrowserRouter} from 'react-router-dom';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <BrowserRouter>
    <Routes>
      <Route path="/" element={<Nav />}>
        <Route index element={<App />}/>
        <Route path="list" element={<DeptList />}/>
        <Route path="add" element={<DeptAdd />}/>
        <Route path="detail" element={<DeptDetail />}/>
      </Route>
    </Routes>
  </BrowserRouter>
);

reportWebVitals();

Components - Nav.js

import React from 'react'
import { Link, Outlet } from 'react-router-dom'

export default function Nav() {
  return (
    <div>
        <nav className="navbar navbar-default">
        <div className="container-fluid">
          <div className="navbar-header">
            <Link className="navbar-brand" to="/">
              비트교육센터
            </Link>
          </div>
          <div className="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
            <ul className="nav navbar-nav">
            <li className="active"><Link to="/">home</Link></li>
            <li><Link to="/list">list</Link></li>
            <li><Link to="/add">add</Link></li>
            <li><Link to="/detail">detail</Link></li>
            </ul>
          </div>
        </div>
        </nav>
        
      <div className="container">
        <Outlet/>
      </div>
    </div>
  )
}

Components - DeptList.js

import React, { useEffect, useState } from 'react'
import { Link } from 'react-router-dom'

export default function DeptList() {
  const[list, setList] = useState([]);
  useEffect(()=>{
    fetch('http://localhost:8080/api/v1/dept')
    .then((response)=> response.json())
    .then((data) => setList(data));
    // const dummy = [
    //   {deptno: 1111, dname: 'tester1', loc: 'test'},
    //   {deptno: 2222, dname: 'tester2', loc: 'test'},
    //   {deptno: 3333, dname: 'tester3', loc: 'test'},
    //   {deptno: 4444, dname: 'tester4', loc: 'test'}
    // ];
    // setList(dummy);
  },[]);
  return (
    <>
    <div>{list.length ?'': <span className='glyphicon glyphicon-refresh' aria-hidden="true"></span> }</div>
    <div className="page-header">
      <h1>list page<small>Subtext for header</small></h1>
    </div>
    <table className='table'>
      <thead>
        <tr>
            <th>deptno</th>
            <th>dname</th>
            <th>loc</th>
        </tr>
      </thead>
      <tbody>
        {/* <tr>
            <td><Link to="/detail">1111</Link></td>
            <td><Link to="/detail">user1</Link></td>
            <td><Link to="/detail">test</Link></td>
        </tr> */}
        {
          list.map(ele => (
          <tr key={ele.deptno}>
              <td><Link to="/detail">{ele.deptno}</Link></td>
              <td><Link to="/detail">{ele.dname}</Link></td>
              <td><Link to="/detail">{ele.loc}</Link></td>
              
          </tr>
          ))
        }
      </tbody>
    </table>

    </>
  )
}

Back - boot

properties

Mysql 과 DB 연결

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.99.100:3306/mydb
spring.datasource.username=scott
spring.datasource.password=tiger

#logging.level.web=debug
logging.level.com.bit.boot03=debug

Vo

Lombok 라이브러리를 사용하여 Vo 정의

@Builder
@AllArgsConstructor
@Data
@NoArgsConstructor
public class DeptVo {
	private int deptno;
	private String dname, loc;
}

Controller

다소 무식한 방법이지만 일단 1차원적으로 메서드에 @CrossOrigin을 달아주면 CORS오류 해결

import ...;

@RestController
@RequestMapping("/api/v1/dept")
@AllArgsConstructor
public class DeptController {
	private final DeptService deptService;
	
	@CrossOrigin
	@GetMapping
	public List<?> getList(){
		return deptService.selectAll();
		
	}
}

Dao

Mybatis를 사용하기 때문에 Dao 인터페이스만 구현하여 Mapper 어노테이션 사용

import ...;

@Mapper
public interface DeptDao {
	@Select("select * from dept")
	List<DeptVo> findAll();
}

Service

@Slf4j
@Service
@AllArgsConstructor
public class DeptService {
	private final SqlSessionFactory sqlSessionFactory;
	
	public List<DeptVo> selectAll() {
		log.debug("call");
		return sqlSessionFactory.openSession().getMapper(DeptDao.class).findAll();
	}
}

Boot03Application

@SpringBootApplication
@MapperScan(basePackageClasses = DeptDao.class)
public class Boot03Application {
	@Autowired
	DataSource dataSource;
	
	public static void main(String[] args) {
		SpringApplication.run(Boot03Application.class, args);
	}
	
	@Bean
	public SqlSessionFactory getSqlSessionFactory() throws Exception {
		SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
		bean.setDataSource(dataSource);
		return bean.getObject(); 
	}
}