프로젝트 구조

com.spring.openapi.data.controller

 DataController

com.spring.openapi.data.service

 DataService

 DataServiceImpl

WEB-INF/views/data 폴더 생성

 

반환값은 data 응답할 문서파일은 xml


 https://apis.data.go.kr/6300000/openapi2022/tourspot/gettourspot?serviceKey=서비스키&pageNo=1&numOfRows=10

name=값&name=값

공백을 값으로 인식하기 때문에 절대로 공백없이

[]괄호가 열리면 0번째.

items:[

{"tourspotNm",

"tourspotZip",...

}

]

items의 0번째가 data값.

확장프로그램으로 데이터 페이지를 보기 편하게 할 수 있다.

 

'spring, springboot' 카테고리의 다른 글

BoardDao와 BoardService 인터페이스의 차이  (0) 2023.09.06
springBoot file  (0) 2023.09.05
Model  (0) 2023.08.24
springBoot project 새로만들기  (0) 2023.08.22
springBoot  (0) 2023.08.21

1.order by 문제:

데이터베이스에서 가장 신경쓰는 부분은 빠르게 처리되는 것, 필요한 양만큼만 데이터를 가져오는 것.

 

빠르게 동작하는 sql을 위해서는 order by를 이용하는 작업을 가능하면 사용하지 말아야한다.

엄청난 성능 저하를 가져오기 때문에

데이터가 적을 경우와 정렬을 빠르게 할 수 있는 방법이 있는 경우가 아니라면 order by는 주의해야만 한다.

 

2. 실행 계획과 order by

실행 계획: sql을 데이터베이스에서 어떻게 처리할 것인가?

 

SQL파싱: 구문에 오류가 있는지, 실행해야하는 대상이 존재하는지

SQL 최적화: 실행되는데 필요한 비용을 계산, 어떤 방식으로 실행하는 것이 가장 좋다는 것을 판단하는 실행계획을 세움

SQL 실행: 메모리상에 데이터를 읽거나 물리적인 공간에서 데이터를 로딩하는 등의 작업

 

2.1 order by 보다 인덱스

인덱스라는 존재가 이미 정렬된 구조이므로 이를 이용해서 별도의 정렬을 하지 않는다.

 

select /*+ INDEX_DESC(spring_board sparing_board_pk) */

*

from spring_board where b_num >0;

1)sort를 하지 않는다.

2)spring_board를 바로 접근하는 것이 아니라 spring_board_pk를 이용해서 접근

 

제약조건명이 인덱스명

 

3.인덱스와 오라클 힌트

오라클은 select문을 전달할 때 힌트라는 것을 사용할 수 있다. 힌트는 개발자가 데이터베이스에 어떤 방식으로 실행해줘야하는지를 명시하기 때문에 조금 강제성이 부여되는 방식이다.

select문을 작성할 때 힌트는 잘못 작성되어도 실행할 때는 무시되기만하고 별도의 에러는 발생하지 않는다.

/*+로 시작하고 */로 끝난다.

FULL(테이블명): /*+FULL(spring_board)*/

INDEX_ASC, INDEX_DESC: /*+INDEX_DESC(spring_board spring_board_pk)*/

DESC(테이블명 인텍스명)

인덱스명: 기본키=> spring_board_pk

 

4.rownum과 인라인 뷰

전체가 아닌 필요한 만큼의 데이터를 가져오는 방식. 오라클 데이터베이스는 페이지 처리를 위해서 rownum이라는 키워드를 사용해서 데이터에 순번을 붙여 사용.

select rownum, b_num, b_name, b_title, to_char(b_data, 'YYYY-MM-DD') as b_date

from spring_board;

 

1번 부터 10번까지

select /*+INDEX_DESC(spring_board spring_board_pk)*/ rownum, b_num, b_name, b_title, to_char(b_data, 'YYYY-MM-DD') as b_date

from spring_board where rownum <=10;

 

2페이지 데이터를 구하기 위해 11번 부터 20번까지의 데이터 출력

select /*+INDEX_DESC(spring_board spring_board_pk)*/ rownum, b_num, b_name, b_title, to_char(b_data, 'YYYY-MM-DD') as b_date

from spring_board where rownum <=20 and rownum >10;

결과는 아무 데이터도 출력하지 않는다. rownum 조건은 반드시 1이 포함되어야한다.

 

select /*+INDEX_DESC(spring_board spring_board_pk)*/ rownum, b_num, b_name, b_title, to_char(b_data, 'YYYY-MM-DD') as b_date

from spring_board where rownum <=20; --반드시 1이 포함되도록 해야한다.

 

인라인 뷰 처리(rownum의 별칭 rnum)

SELECT 

rnum, b_num, b_name, b_title, to_char(b_data, 'YYYY-MM-DD') as b_date

from(  //안쪽에는 함수 쓰면 안됨.

select /*+INDEX_DESC(spring_board spring_board_pk)*/

rownum asrnum, b_num, b_name, b_title, b_date

from spring_board

where rownum <=20

) boardlist

where rnum >10;

 

--순서정리

필요한 순서로 정렬된 데이터에 rownum을 붙인다.

처음부터 해당 페이지의 데이터를 rownum <=20과 같은 조건을 이용해서 구한다.

구해놓은 데이터를 하나의 테이블처럼 간주하고 인라인뷰로 처리한다.

인라인뷰에서 필요한 데이터만을 남긴다.

 

페이징 처리

페이징 처리를 위해서 필요한 파라미터: 페이지 번호, 한 페이지당 몇 개의 데이터를 보여줄 것인지가 결정되어야 한다.

 

pageNum*amount

(pageNum-1)*amount

 

화면에 페이징처리

화면 페이지는 다음과 같은 정보들이 필요

현재 페이지 번호, 이전과 다음으로 이동 가능한 링크의 표시 여부, 화면에서 보여지는 페이지의 시작번호와 끝번호

 

5페이지를 본다면 1페이지 부터 10페이지 안에서의 5페이지, 15페이지를 본다면 11페이지 부터 시작

페이징의 끝번호(endPage) 계산

this.endPage = (int)(Math.ceil(cvo.getPageNum()/10.0))*10;

-1페이지: Math.ceil(0.1)*10 = 10

-2페이지: Math.ceil(0.2)*10 = 10

-10페이지: Math.ceil(1)*10 = 10

-11페이지: Math.ceil(1.1)*10 = 20

-20페이지: Math.ceil(2)*10 = 20

 

페이징의 시작번호 계산

this.startPage = this.endPage-9;

끝번호(endPage)는 전체 데이터수에 의해서 영향을 받는다.

int realEnd = (int) (Math,ceil(total*1.0)/cvo.getAmount()));

if(realEnd <= this.endPage){

this.endPage = realEnd;

}

'DataBase+SQL' 카테고리의 다른 글

PROCEDURE TRIGGER  (0) 2023.06.29
PreparedStatement  (0) 2023.06.28
SQL 반복문, 커서  (0) 2023.06.27
PL/SQL  (0) 2023.06.26
MVIEW, 시퀀스  (0) 2023.06.23

스프링 부트(Spring Boot)는 자바 기반의 웹 애플리케이션을 개발하기 위한 프레임워크로서, 스프링(Spring) 프레임워크를 기반으로 하며 개발자들이 더 간편하게 웹 애플리케이션을 구축하고 실행할 수 있도록 도와줍니다. 스프링 부트는 웹 애플리케이션의 여러 측면을 다루는 다양한 기능과 클래스를 제공하는데, 그 중에서도 Model 클래스는 웹 애플리케이션의 뷰(View)와 컨트롤러(Controller) 사이에서 데이터를 전달하는 역할을 합니다.

Model은 스프링의 MVC 아키텍처(Model-View-Controller)에서 사용되는 개념 중 하나로, 다음과 같은 역할을 수행합니다:

  1. 데이터 전달: Model은 컨트롤러(Controller)에서 처리한 데이터를 뷰(View)로 전달하는 데 사용됩니다. 이는 클라이언트에게 보여질 데이터나 웹 페이지의 내용 등을 포함합니다.
  2. 데이터 저장: Model은 뷰에서 전달된 데이터를 임시로 저장하는 역할을 수행합니다. 이렇게 저장된 데이터는 뷰에서 필요한 경우에 사용될 수 있습니다.
  3. 뷰에 데이터 제공: 뷰는 Model에서 제공한 데이터를 사용하여 웹 페이지를 동적으로 구성하고, 사용자에게 적절한 정보를 보여줍니다.

스프링 부트에서 Model 객체는 주로 컨트롤러 메서드의 매개변수로 선언되며, 메서드 내에서 데이터를 담고 뷰로 전달하는 역할을 수행합니다. 일반적으로 Model은 키-값 쌍으로 데이터를 저장하며, 이 데이터는 뷰에서 해당 키를 사용하여 접근할 수 있습니다.

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class MyController {
    @GetMapping("/hello")
    public String hello(Model model) {
        String message = "Hello, Spring Boot!";
        model.addAttribute("greeting", message); // 데이터를 Model에 추가
        
        return "helloPage"; // 뷰의 이름을 반환
    }
}
src/
└── main/
    ├── java/
    │   └── com/
    │       └── example/
    │           ├── MyApplication.java
    │           ├── MyController.java
    ├── resources/
    │   └── application.properties
    └── webapp/
        └── WEB-INF/
            └── views/
                └── helloPage.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
    <title>Hello Page</title>
</head>
<body>
    <h1>${greeting}</h1>
</body>
</html>
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class MyController {
    @GetMapping("/hello")
    public String hello(Model model) {
        String message = "Hello, Spring Boot!";
        model.addAttribute("greeting", message);
        
        return "helloPage";
    }
}

application.properties

spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp

 

SpringBoot 작성 순서.

  1. VO (Value Object) 작성:
    • 데이터를 저장하고 전달하기 위한 객체를 정의하는 클래스를 작성합니다.
    • 필요한 데이터 필드와 getter/setter 메서드를 포함하여 작성합니다.
    • 주로 데이터베이스의 테이블과 매핑될 수 있습니다.
  2. DAO (Data Access Object) 작성:
    • 데이터베이스와의 상호 작용을 처리하는 클래스를 작성합니다.
    • 데이터베이스에 데이터를 조회, 삽입, 갱신, 삭제하는 메서드를 작성합니다.
    • 주로 JPA, MyBatis 등의 ORM(Object-Relational Mapping) 기술을 사용하여 구현합니다.
  3. Service 클래스 작성:
    • 비즈니스 로직을 수행하는 서비스 클래스를 작성합니다.
    • DAO를 이용하여 데이터베이스와의 상호 작용을 호출하고, 비즈니스 로직을 구현합니다.
    • 트랜잭션 처리, 데이터 가공, 복잡한 비즈니스 규칙 등을 처리합니다.
  4. 컨트롤러(Controller) 클래스 작성:
    • 사용자의 요청을 처리하는 컨트롤러 클래스를 작성합니다.
    • @Controller 어노테이션을 추가하여 컨트롤러임을 명시합니다.
    • 각 요청에 대한 처리 메서드를 작성하고, Service 레이어를 호출하여 비즈니스 로직을 수행합니다.
    • 필요한 경우에는 모델(Model)을 생성하고 뷰 이름을 반환합니다.
  5. 뷰(View) 템플릿 작성:
    • 사용자에게 보여질 웹 페이지의 모습을 정의하는 뷰 템플릿을 작성합니다.
    • Thymeleaf, JSP, Freemarker 등을 사용하여 뷰 템플릿을 구현합니다.
  6. 스프링 설정 및 라우팅:
    • application.properties나 application.yml 파일을 통해 애플리케이션 설정을 구성합니다.
    • 컨트롤러의 요청 매핑과 뷰 리졸버 설정을 등록합니다.
  7. 실행 및 테스트:
    • 애플리케이션을 실행하여 웹 서버를 시작합니다.
    • 웹 브라우저나 API 클라이언트를 통해 컨트롤러가 응답하는지 테스트합니다.
     

 

pom.xml에 라이브러리 설정 필요한 것만 세팅이 안되어 있으면 오류.

web, test, db, jsp...

시험볼 때 tiles setting 가져오면 안됨. devProject: pom.xml, 내용은 spring쪽

가장기본적인것, control 단과 뷰단을 만들고 매핑해서 보이면 service, dao로 다음으로 xml 

control단, 뷰(.jsp)로 갈수 있는 메서드 하나(매핑)만 만들고 뷰단이 보이는지

패키지 잘못쓰면 삭제 후 다시 만들기

패키지 하위에 만들기

5번하기 전에 Mybatis 설정파일만 이름을 다줘서 세팅

config는 mybatis 설정파일

그다음 properties 수정

 

 

 

'spring, springboot' 카테고리의 다른 글

springBoot file  (0) 2023.09.05
스프링 부트 api  (0) 2023.09.04
springBoot project 새로만들기  (0) 2023.08.22
springBoot  (0) 2023.08.21
Array, ArrayList,sampleDTOList  (0) 2023.08.18

https://congsong.tistory.com/15

 

스프링 부트(Spring Boot) - 게시판 CRUD 처리하기 [Thymeleaf, MariaDB, IntelliJ, Gradle, MyBatis]

본 게시판 프로젝트는 단계별(step by step)로 진행되니, 이전 단계를 진행하시는 것을 권장드립니다. 본 포스팅은 DBeaver를 기준으로 작성된 글이며, 만약 MariaDB가 설치되어 있지 않으시다면, 선행

congsong.tistory.com

 

프로젝트 생성 후 아래의 내용 순으로 진행하시면 됩니다. 참고해 주세요.
1. 프로젝트 생성 2. pom.xml 설정 3. application.properties 환경설정 파일인 properties 파일 인코딩 변경(UTF-8) 4. 패키지 생성 및 폴더 생성 및 환경 설정에 필요한 파일 생성 5.  application.properties 수정 6. 클래스 및 인터페이스 생성.

'spring, springboot' 카테고리의 다른 글

스프링 부트 api  (0) 2023.09.04
Model  (0) 2023.08.24
springBoot  (0) 2023.08.21
Array, ArrayList,sampleDTOList  (0) 2023.08.18
springBoot  (0) 2023.08.17

src/main/java

com.boot.example.dao

 SubjectDAO =>인터페이스 생성

src/test/java

com.boot.example.dao

 SubjectTests => 단위테스트를 위한 클래스 생성

com.boot.example.service

 SubjectService =>인터페이스 생성(다형성 - 결합도가 낮은 프로그램)

 SubjectServiceImpl => 구현 클래스 생성

com.boot.example.controller

 SubjectController => controller 클래스 생성

 공통 매핑: /subject/로 명시

 

학과 리스트 매핑: /subject/subjectList

뷰단: /WEB-INF/views/subject/subjectList.jsp

학과 입력 매핑: /subject/subjectInsert

 

application.properties에 위치 설정

# 뷰리졸버 설정

spring.mvc.view.prefix=/WEB-INF/views/

spring.mvc.view.suffix=.jsp

return 앞에 prefix, 뒤에 suffix

 

src/main/java: 로직을 실행시키기 위한 폴더.

src/test/java: 단위테스트를 위한 폴더로 로직이 나오지 않음.

 

1.controller

인스턴스가 제일 먼저 만들어져야함.

어노테이션을 통해 @Controller (인스턴스 생성)

 

1-1.서비스를 구현해서 구현클래스를 통해 인스턴스 생성

@Service

1-2.

controller에 subject매핑

@RequestMapping("/subject/*")

@Slf4j:controller에 원래 안씀, 연습하기 위해 사용

1-3.매핑 /subjectList

서비스가 subjectList 호출

데이터가 아니라 뷰

package com.boot.example.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import com.boot.example.domain.SubjectVO;
import com.boot.example.service.SubjectService;

import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

@Controller
@RequestMapping("/subject/*")
@Slf4j
public class SubjectController {
	
	@Setter(onMethod_=@Autowired)
	private SubjectService subjectService;
	
	@GetMapping("/subjectList")
	public String subjectList(Model model) {
		log.info("subjectList 메서드 호출");
		
		List<SubjectVO> subjectList = subjectService.subjectList();
		model.addAttribute("subjectList", subjectList);	//속성 추가
		
		return "subject/subjectList";
	}

}

 

2.service는 DAO가 필요

serviceImpl에서 만든 subjectList를 controller에서 받음.

package com.boot.example.service;
import java.util.List;

import com.boot.example.domain.SubjectVO;
public interface SubjectService {
	public List<SubjectVO> subjectList();

}
package com.boot.example.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.boot.example.dao.SubjectDAO;
import com.boot.example.domain.SubjectVO;

import lombok.Setter;

@Service
public class SubjectServiceImpl implements SubjectService{

	//service 는 DAO없이는 안되고
	//control단은 service 없이는 안됨.
	@Setter(onMethod_=@Autowired)
	private SubjectDAO subjectDAO;
	
	@Override
	public List<SubjectVO> subjectList() {
		List<SubjectVO> list = null;
		list = subjectDAO.subjectList();
		return list;
	}

}

인터페이스를 구현해서 사용하면되니 개발자가 구현 클래스만 수정하면 됨.

결합도가 낮으면 수정하기 쉬워야함.

 

단위테스트를 하는게 훨씬 속도가 빠르다.

입력, 수정, 삭제는 사용 후 다시 불러와야하므로 redirect 사용

:/

'spring, springboot' 카테고리의 다른 글

Model  (0) 2023.08.24
springBoot project 새로만들기  (0) 2023.08.22
Array, ArrayList,sampleDTOList  (0) 2023.08.18
springBoot  (0) 2023.08.17
spring(의존성 주입(DI))  (0) 2023.08.16
import React, { useState, useRef, useEffect } from 'react';
import './Modal.css';

const Modal = () => {
  const dialogRef = useRef();
  const [isOpen, setOpen] = useState(false);

  const handleOpenModal = () => {
    dialogRef.current.showModal();
    setOpen(true);
  };

  const handleCloseModal = () => {
    dialogRef.current.close();
    setOpen(false);
  };

  const handleOverlayClick = (event) => {
    if (event.target === dialogRef.current) {
      handleCloseModal();
    }
  };

  useEffect(() => {
    window.addEventListener('click', handleOverlayClick);
    return () => {
      window.removeEventListener('click', handleOverlayClick);
    };
  }, [isOpen]);

  return (
    <div>
      <button onClick={handleOpenModal}>Open Modal</button>
      <dialog ref={dialogRef} open={isOpen}>
        <button className="close-button" onClick={handleCloseModal}>
          X
        </button>
        <h2>Modal Content</h2>
        <p>This is a modal example using the dialog element tag.</p>
        <button className="next-button" onClick={handleCloseModal}>
          다음으로 이동하기
        </button>
      </dialog>
    </div>
  );
};

export default Modal;
import React, { useState } from "react";
import "./Body.css";

import ItemList from "./ItemList";
import ItemTotal from "./ItemTotal ";
import ItemInput from "./ItemInput";
import Modal from "./Modal";
import Reward from "./Reward";

function Viewer({ number }) {
  return <div>{number % 2 === 0 ? <h3>짝수</h3> : <h3>홀수</h3>}</div>;
}
const Body = () => {
  const [number, setNumber] = useState(1);

  const onIncrease = () => {
    setNumber(number + 1);
  };
  const onDecrease = () => {
    if (number > 1) {
      setNumber(number - 1);
      onChangePrice(number - 1);
    }
  };

  const itemDateArray = [
    {
      name: "비왈츠 로지 보틀",
      price: 33500,
      number: 1,
    },
  ];

  const [items, setItems] = useState(itemDateArray);

  const onChangePrice = (name, newNumber) => {
    console.log("onChangePrice name : " + name + "number : " + number);

    setItems((prevItems) =>
    prevItems.map((item) =>
      item.name === name ? { ...item, number: newNumber } : item
      )
    );
  };

  const onQuantityChange = (event) => {
    const newNumber = parseInt(event.target.value);
    if (!isNaN(newNumber) && newNumber >= 1) {
      setNumber(newNumber);
      onChangePrice("비왈츠 로지 보틀",newNumber);  // 기본적으로 "비왈츠 로지 보틀"에 대해서 처리
    } else {
      setNumber("");
      onChangePrice(0);
    }
  };
  const handleBlur = () => {
    if (!number || isNaN(number) || number < 1) {
      alert("수량을 올바르게 입력해 주세요.");
      setNumber(1);
      onChangePrice("비왈츠 로지 보틀", 1);
    }
  };
  return (
    <div class="body">
      <div class="purchase-title">프로젝트 이름</div>
      <div id="purchaseWrap">
        <div id="purchase-step">
          <div class="container">
            <div class="circle-container">
              <div class="circle_selected circle">
                <div class="word">리워드 선택</div>
              </div>
            </div>

            <div class="circle-container">
              <div class="circle">
                <div class="word">결제 하기</div>
              </div>
            </div>

            <div class="circle-container">
              <div class="circle">
                <div class="word">결제 완료</div>
              </div>
            </div>
          </div>
          <div class="connector"></div>
        </div>
        <h4>리워드 선택</h4>
        <div class="rewardItemPrice">
          <input type="checkbox" name="reward1" value="33,500" />
          <span>33,500</span>
          &nbsp;원
        </div>
        <div class="rewardItemContent">
          <div class="rewardItemTitle">비왈츠 로지 보틀</div>
          <span class="rewardItemMax">최대 구매 가능 수량 500개</span>
          <div class="rewardItemDesciption">
            14%할인 39,000원 - 33,500원
            <br />
            비왈츠 로지 보틀 1병
          </div>
          <div class="rewardDelivery">
            <img src=""></img>
            <span>배송비 3,000원</span>
            <span class="rewardDeliveryDivide"> | </span>
            <span>2023년 09월 중순 리워드 제공 예정</span>
            <ul>
              <li class="rewardItem">
                <div class="rewardCardName">
                  비왈츠 로지 보틀
                  <button type="button" class="rewardCardCancel">
                    x
                  </button>
                </div>
                <div class="rewardCardContent">
                  <div class="ItemQuantityContainer">
                    <button onClick={onDecrease}> - </button>
                    <input
                      type="number"
                      value={number}
                      name="quantity"
                      min="1"
                      onChange={onQuantityChange}
                      onBlur={handleBlur}
                    />
                    <button onClick={onIncrease}> + </button>
                  </div>
                  <p class="rewardCardPrice"></p>
                </div>
              </li>
            </ul>
          </div>
        </div>

        <h2>{number}</h2>
        <Viewer number={number} />

        <Reward />
        <Modal />
        <div class="circle1"></div>
        <div class="dotted-line"></div>

        <ItemInput
        items={items}
        onChangePrice={onChangePrice}
        number={number}
        onQuantityChange={onQuantityChange}
        onIncrease={onIncrease}
        onDecrease={onDecrease}
        onBlur={handleBlur}
      />
        <hr />
        <ItemList items={items} number={number}  />
        <ItemTotal items={items} number={number} />
      </div>
    </div>
  );
};

export default Body;

 

어려웠던 부분

도형 사이를 잇는 점선 구현

input type="text"에 값을 정해놨더니 직접 수정이 안되는 것

질문할 것
1. input type text로 했는데 value값을 {number}로 넣었더니 텍스트 직접 수정 불가능
2. {number}의 값을 number에 대입하고 싶은데 안됨.
3. 수량 Maximum과 Mininum 제한하는 법

4.도형 사이를 잇는 점선 구현

 

Modal을 이용해 팝업창 구현하기

 

터미널에 입력

npm install react-modal

 

=>모달 라이브러리를 사용할 수 있게 되었다.

 

import Modal from 'react-modal';

로 import

Modal이라는 객체는 isOpen을 필수적으로 가지고 있어야한다.

isOpen값은 true/false여야한다.

https://curryyou.tistory.com/533

어려웠던 부분

도형 사이를 잇는 점선 구현

input type="text"에 값을 정해놨더니 직접 수정이 안되는 것

질문할 것
1. input type text로 했는데 value값을 {number}로 넣었더니 텍스트 직접 수정 불가능
2. {number}의 값을 number에 대입하고 싶은데 안됨.
3. 수량 Maximum과 Mininum 제한하는 법

4.도형 사이를 잇는 점선 구현

 

Modal을 이용해 팝업창 구현하기

 

터미널에 입력

npm install react-modal

 

=>모달 라이브러리를 사용할 수 있게 되었다.

 

import Modal from 'react-modal';

로 import

Modal이라는 객체는 isOpen을 필수적으로 가지고 있어야한다.

isOpen값은 true/false여야한다.

https://curryyou.tistory.com/533

+ Recent posts