전자정부프레임워크 4 - react / boot

akanana·2023년 6월 2일
0

외부 라이브러리 사용

npm i react-dropdown
import Dropdown from 'react-dropdown';
import 'react-dropdown/style.css';
...
const options = [
  'one', 'two', 'three'
];
const defaultOption = options[0];
...
<Dropdown options={options} value={defaultOption} placeholder="Select an option" />

react-dropdown에서 css를 위한 추가 옵션들을 확인 가능하다

구현

<button onClick={openStartTimeDropDown}>클릭</button>
<article 
	className={
      `components-dropdown ${dropdownVisibility 
      ? 'slide-fade-in-dropdown' 
      : 'slide-fade-out-dropdown'}`}
>
{dropdownVisibility ?
<ul>
  {infos.map((info) => {
    let data = (
      <li key={info} onClick={selectStartTime(info)}>
      	{info}
      </li>
    );
    return data;
  })}
</ul>
: null}
</article>
/* drop down animation */
@keyframes slide-fade-in-dropdown-animation {
	0% {
		transform: translateY(-100%);
	}

	100% {
		transform: translateY(0);
	}
}

.slide-fade-in-dropdown {
	overflow: hidden;
}

.slide-fade-in-dropdown > * {
	animation: slide-fade-in-dropdown-animation .4s ease;
	float: inline-start;
}

/* fade out */

@keyframes slide-fade-out-dropdown-animation {
	0% {
		transform: translateY(0);
	}

	100% {
		transform: translateY(-100%);
	}
}

.slide-fade-out-dropdown {
	overflow: hidden;
}

.slide-fade-out-dropdown > * {
	animation: slide-fade-out-dropdown-animation 0.4s ease;
	animation-fill-mode: forwards;
}
.components-dropdown > div {
	position: relative;
}
.components-dropdown > ul > li{
	list-style: none;
	background-color: #fff;
	position: relative;
	padding-top: 0.2em;
}
.components-dropdown > ul > li:hover{
	background-color: gray;
	color: #fff;
}
.components-dropdown > ul {
	height: 10em;
	overflow-x: scroll;
	margin: 0;
	padding: 0;
}

외부 라이브러리 사용

npm i react-modal
import Modal from 'react-modal';
...
let subtitle;
const [modalIsOpen, setIsOpen] = useState(false);

function openModal() {
  setIsOpen(true);
}

function afterOpenModal() {
  // references are now sync'd and can be accessed.
  subtitle.style.color = '#f00';
}

function closeModal() {
  setIsOpen(false);
}
...
<button onClick={openModal}>Open Modal</button>
<Modal
  isOpen={modalIsOpen}
  onAfterOpen={afterOpenModal}
  onRequestClose={closeModal}
  contentLabel="Example Modal"
>
  <h2 ref={(_subtitle) => (subtitle = _subtitle)}>Hello</h2>
  <button onClick={closeModal}>close</button>
  <div>I am a modal</div>
  <form>
    <input />
    <button>tab navigation</button>
  <button>stays</button>
  <button>inside</button>
  <button>the modal</button>
  </form>
</Modal>

react-modal에서 기타 사용법등을 확인 가능하다

구현

App.js

import Modal from './modal'
...
const [detailOpen, setDetailOpen] = useState(false)
const [data, setData] = useState()
const openDetail = () =>{
  setDetailOpen(true)
}
const closeDetail = () =>{
  setDetailOpen(false)
}
...
<button onClick={openDetail()}>Open</button>
<Modal open={ detailOpen } close={ closeDetail }/>
</div>

Modal.js

const { open, close } = props;
...
{open ? (
<section>
  <header>
     추가하기
  	<button className="close" onClick={close}>&times;</button>
  </header>
  <div>

  </div>
  <footer>
    <button className="close" onClick={close}>
      close
  	</button>
  </footer>
</section>
) : null}
.modal {
display: none;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 99;
background-color: rgba(0, 0, 0, 0.6);
}
.modal > button {
outline: none;
cursor: pointer;
border: 0;
}
.modal > section {
width: 90%;
max-width: 450px;
margin: 0 auto;
border-radius: 0.3rem;
background-color: #fff;
/* 팝업이 열릴때 스르륵 열리는 효과 */
animation: modal-show 0.3s;
overflow: hidden;
}
.modal > section > header {
position: relative;
padding: 16px 64px 16px 16px;
background-color: #f1f1f1;
font-weight: 700;
}
.modal > section > header button {
position: absolute;
top: 15px;
right: 15px;
width: 30px;
font-size: 21px;
font-weight: 700;
text-align: center;
color: #999;
background-color: transparent;
}
.modal > section > main {
padding: 16px;
border-bottom: 1px solid #dee2e6;
border-top: 1px solid #dee2e6;
}
.modal > section > footer {
padding: 12px 16px;
text-align: right;
}
.modal > section > footer button {
padding: 6px 12px;
color: #fff;
background-color: #6c757d;
border-radius: 5px;
font-size: 13px;
}
.modal.openModal {
display: flex;
align-items: center;
/* 팝업이 열릴때 스르륵 열리는 효과 */
animation: modal-bg-show 0.3s;
}
@keyframes modal-show {
from {
    opacity: 0;
    margin-top: -50px;
}
to {
    opacity: 1;
    margin-top: 0;
}
}
@keyframes modal-bg-show {
from {
    opacity: 0;
}
to {
    opacity: 1;
}
}

위 두 코드를 재작성시에는 라이브러리에 위 css를 조금 첨가하여 사용할 것 같다.
하지만 react를 처음 접하는 나에게 2개의 코드를 통해 가능을 구현하는것이 react 구조를 이해하는데에 큰 도움이 되었으므로 한 번쯤은 꼭 사용해보기를 추천한다.

✔file

file 저장

흐름

react =파일전송=> spring
react        spring (파일저장)

구현
const onChange = e =>{
    const reader = new FileReader()
    reader.readAsDataURL(e.target.files[0]);
    setImgFile(e.target.files[0])
    
    return new Promise((res)=>{
        reader.onload = () =>{
            setImg(reader.result)
            res()
        }
    })
}
const onClick = async e => {
    const formData = new FormData()
    let imsiFileName = '';
    formData.append('file',imgFile)

    let options = {
        headers: {
            'Content-type': 'multipart/form-data'
        },
        withCredentials: true
    };
    try {
        imsiFileName = await axios.post("http://localhost:18080/file/save",formData,options)
    } catch (error) {
        alert("에러 발생, 빈칸을 채워주세요_file")
    }
  	// 테스트를 위해 별개의 axios로 분리하였습니다.
    // 위는 file을 upload 하기위한 axios
    // 아래는 file의 url을 DB에 저장하기 위한 별도의 axios 입니다
    const data = {
        imglink : imsiFileName.data
    }
    options = {
        headers: {
            'Content-Type': 'application/json'
        },
        withCredentials: true
    };
    try {
        await axios.post("http://localhost:18080/loc/save",data,options)
        window.location.replace("/loc")
    } catch (error) {
        alert("에러 발생, 빈칸을 채워주세요_loc")
    }
}
...
<div className='pre-img'>
	<img src={img?img:'https://via.placeholder.com/300'} alt='/300.png' />
</div>
<input className='input-img' type="file" onChange={onChange}/>
<button className='save-loc' onClick={onClick}>업로드</button>

FileController.java

@PostMapping("/save")
public String doSave(@RequestParam("file") MultipartFile file) {
    log.info("/save : {}", file);
    String fileName = file.getOriginalFilename();
    String extension = fileName.substring(fileName.lastIndexOf("."), fileName.length());
    UUID uuid = UUID.randomUUID();
    String newFileName = uuid.toString() + extension;
    log.info(">> {}", newFileName);
    File targetFile = new File("src/main/resources/static/imgs/" + newFileName);
    log.info(">> : {}", targetFile.getAbsolutePath());
    try {
        InputStream inputStream = file.getInputStream();
        FileUtils.copyInputStreamToFile(inputStream, targetFile);
    } catch (Exception e) {
        FileUtils.deleteQuietly(targetFile);
        log.error("error : ", e);
    }
    return newFileName;
}

file 호출

흐름

react =요청 전송=> spring
react <=파일 전송= spring

구현
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addResourceHandlers(final ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/imgs/**")
                .addResourceLocations("classpath:/static/imgs/");
    }

}
<img src={`http://localhost:18080/imgs/${imgurl}`} alt=''/>

0개의 댓글