# react state에 관한 설명

ko.reactjs.org/docs/state-and-lifecycle.html

 

State and Lifecycle – React

A JavaScript library for building user interfaces

ko.reactjs.org


- react에서 state을 사용한 데이터 관리 


- 폴더 

 


1.  class 방식

// class 방식일 경우 
import React, { Component, PureComponent } from 'react';

class StateTest extends Component {
    state = {
        text: 'state에서 text 알아보기',
        num: 0
    };

    componentDidMount() {
        console.log('=============================componentDidMount');
    }

    // 리렌더링할 요소를 지정
    shouldComponentUpdate(nextProps, nextState, nextContext) {
        return true;
    }

    // 리렌더링의 경우
    componentDidUpdate(prevProps, prevState, snapshot) {
        console.log('=============================componentDidUpdate');
    }

    // 컴포넌트가 제거되기 전 , 비동기 요청 정리를 많이함
    componentWillUnmount() {
        console.log('=============================componentWillUnmount');
    }

    // 값에 변화주기 
    onNumBtn = (sign) => (e) => {
        if (sign === '+') {
            // setState에서는 값을 직접적으로 참조가 불가능함으로 함수로 리턴 형식으로 함.
            this.setState((pre) => {
                console.log(pre);
                return {
                    text: '증감',
                    num: pre.num + 1
                }
            });
        } else {
            this.setState((pre) => {
                console.log(pre);
                return {
                    text: '감소',
                    num: pre.num - 1
                }
            });
        }
    };

    render() {
        // state 값 가져오기
        const { text, num } = this.state;
        return (
            <>
                <h2 id='test'>{text}</h2>
                <h2 id='num'>{num}</h2>
                <div>
                    <button onClick={this.onNumBtn('+')}>+</button>
                    <button onClick={this.onNumBtn('-')}>-</button>
                </div>

            </>
        );
    };
}
export default StateTest;

 

 

2.  Hooks 방식

// class 방식일 경우 
// import React, { Component, PureComponent } from 'react';
import React, { memo, useEffect, useRef, useState } from 'react';

const StateTest = memo(() => {
    const [text, setText] = useState('Hooks Text Display');
    const [num, setNum] = useState(0);
    const [inputVal, setInputVal] = useState('input text');
    const input = useRef();

    useEffect(() => {
        console.log('num = ', num);
        console.log('text = ', text);
        input.current.focus();
    }, [text, num, input]);

    const onNumBtn = (sign) => (e) => {
        if (sign === '+') {
            setText('증감');
            setNum((pre) => {
                return pre + 1;
            });
        } else {
            setText('감소');
            setNum((pre) => {
                return pre - 1;
            });
        }
    };

    const onInputValChange = (e) => {
        setInputVal(e.target.value);
    };

    return (
        <>
            <h2 id='test'>{text}</h2>
            <h2 id='num'>{num}</h2>
            <div>
                <button onClick={onNumBtn('+')}>+</button>
                <button onClick={onNumBtn('-')}>-</button>
            </div>
            <input type='text' value={inputVal} onChange={onInputValChange} ref={input}></input>
            <h1>{inputVal}</h1>
        </>
    );
});

export default StateTest;

3. index.jsx

// import 방법과 const 방법 . 
// 한방향으로 지정해서 프로젝트 이끌어가여함 안그러면 충돌남.
// const React = require('react');
import React from 'react';

// # https://ko.reactjs.org/docs/react-dom.html
// react-dom package는 앱의 최상위 레벨에서 사용할 수 있는 DOM에 특화된 메서드와 필요한 경우 React 모델 외부로 나갈 수 있는 해결책을 제공
// render(), hydrate(), unmountComponentAtNode(), findDOMNode(), createPortal()
// const ReactDom = require('react-dom');
import ReactDom from 'react-dom';

// # https://gist.github.com/velopert/21896de87566628b38b7ecb95973a7b3
// 코드가 변경되었을 때 페이지를 새로고침하지 않고 바뀐 부분만 빠르게 교체해주는 라이브러리
// const { hot } = require('react-hot-loader/root');
import { hot } from 'react-hot-loader/root';

// 불러올 컴포넌트의 이름
// import ResponseCheck from "./ResponseCheck";
// import RSP from "./RSP";
import StateTest from './component/StateTest';
const Hot = hot(StateTest);

ReactDom.render(<Hot />, document.querySelector('#root'));


 

 

4. webpack.config.js

const path = require('path');
const webpack = require('webpack');
// process.env.NODE_ENV = 'production';

module.exports = {
    // 프로젝트 이름 , 카멜 케이스는 안됨, 두 단어 이상을 사용한 프로젝트 이름 
    name: 'state-test', 
    mode: 'development', // 실서비스 : productuon
    // mode: 'productuon', // 실서비스 : productuon
    devtool: 'eval', // 빠르게
    resolve: {
        // 파일 형식 
        extensions: ['.js', '.jsx']
    },
    entry: {
        app: ['./index']
    }, // 입력
    module: {
        rules: [{
            test: /\.jsx?/,
            loader: 'babel-loader',
            options: {
                presets: [
                    ['@babel/preset-env', {
                        targets: {
                            browsers: ['> 5% in KR']
                        },
                        debug: true,
                    }],
                    '@babel/preset-react'],
                plugins: [
                    '@babel/plugin-syntax-class-properties',
                    "transform-class-properties",
                    'react-hot-loader/babel'
                ]
            }
        }]
    },
    plugins: [
        new webpack.LoaderOptionsPlugin({ debug: true }),
    ],
    output: {
        // webpack-dev-server에서 hot 로드 되기 위해서 위치 지정, 없을 경우 html에서 ./dist/app.js를 app.js로 변경해야함
        publicPath: '/dist/',
        path: path.join(__dirname, 'dist'),
        filename: 'app.js'
    }, // 출력
    devServer: {
        // contentBase: path.join(__dirname, "dist"),
        inline: true,
        hot: true,
        port: 9000,
        hot:true
    },

}

5. package.json

{
  "name": "number-baseball",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "webpack-dev-server"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "react": "^16.14.0",
    "react-dom": "^16.14.0"
  },
  "devDependencies": {
    "@babel/plugin-syntax-class-properties": "^7.12.1",
    "babel-plugin-transform-class-properties": "^6.24.1",
    "@babel/core": "^7.11.6",
    "@babel/preset-env": "^7.11.5",
    "@babel/preset-react": "^7.10.4",
    "babel-loader": "^8.1.0",
    "react-hot-loader": "^4.12.21",
    "webpack": "^4.44.2",
    "webpack-cli": "^3.3.12",
    "webpack-dev-server": "^3.11.0"
  }
}

 

블로그 이미지

미나미나미

,

1. 컨트롤 부분

	
	@Autowired
	ResourceLoader resourceLoader;
	
    // 파일의 위치
	@Value("${file.path}")
	private String file_Path;

    @RequestMapping("/fileDownload")
	public ResponseEntity<InputStreamResource> download(
			@RequestParam(defaultValue = "test") String fName
			) throws IOException {
		
		System.out.println("fName = " + fName);
		// 파일 경로 
		String path = file_Path + File.separator + fName;
		// 파일 존재 유무 
		boolean fExist = _FileUtil.fileExistInfo(path);
		if(fExist) {
	        File file = new File(path);
	        String fileName = file.getName();
	        // 파일 확장자 
	        String ext = fileName.substring(fileName.lastIndexOf(".") + 1);
	        HttpHeaders header = new HttpHeaders();
	        Path fPath = Paths.get(file.getAbsolutePath());
	        
	        header.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename="+fileName);
	        header.add("Cache-Control", "no-cache, no-store, must-revalidate");
	        header.add("Pragma", "no-cache");
	        header.add("Expires", "0");
	        
	        // 대용량일 경우 resource3을 사용해야함 
//	        ByteArrayResource resource = new ByteArrayResource(Files.readAllBytes(fPath ));
//	        Resource resouce2 = resourceLoader.getResource(path); 
	        InputStreamResource resource3 = new InputStreamResource(new FileInputStream(file));
	        
	        return ResponseEntity.ok()
	                .headers(header)
	                .contentLength(file.length())
	                .contentType(MediaType.parseMediaType("application/octet-stream"))
	                .body(resource3);
		}
		return null;
	}

 

 

2. 테스트 하기 

 # 1.zip 파일 다운 받기


 

블로그 이미지

미나미나미

,

# [Spring MVC] thymeleaf 적용하기

1. pom.xml 

<dependency>
	<groupId>org.thymeleaf</groupId>
	<artifactId>thymeleaf-spring5</artifactId>
	<version>3.0.11.RELEASE</version>
</dependency>

2. servlet-context.xml 설정 

 # 만약 thymelead 한글 깨진 다면,  아래의 UTF-8 들어간 부분을 확인하시길 바랍니다.

<beans:property name="characterEncoding" value="UTF-8" />

<beans:property name="fileEncoding" value="UTF-8" />

	<!-- 기존 jsp로 화면 설계 부분 -->
	<!-- 
	<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> 
		<beans:property name="prefix" value="/WEB-INF/views/" /> <beans:property 
		name="suffix" value=".jsp" /> </beans:bean> 
	-->
	
	<!-- thymeleaf 설정 부분  -->
	<beans:bean id="templateResolver"
		class="org.thymeleaf.templateresolver.ServletContextTemplateResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".html" />
		<beans:property name="templateMode" value="HTML5" />
		<beans:property name="cacheable" value="false" />
		<beans:property name="characterEncoding" value="UTF-8" />
	</beans:bean>

	<beans:bean id="templateEngine"
		class="org.thymeleaf.spring5.SpringTemplateEngine">
		<beans:property name="templateResolver"
			ref="templateResolver" />
	</beans:bean>

	<beans:bean
		class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
		<beans:property name="templateEngine"
			ref="templateEngine" />
		<beans:property name="characterEncoding" value="UTF-8" />
	</beans:bean>

	<beans:bean
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<beans:property name="location"
			value="/resources/config/application.config" />
		<beans:property name="fileEncoding" value="UTF-8" />
	</beans:bean>
	<!-- thymeleaf 설정 부분  -->

3. 페이지 

<!DOCTYPE html>

<!-- thymelead 불러오기 -->
<html lang="ko" xmlns:th="http://www.thymeleaf.org">

<head>
    <meta charset="UTF-8">
	<meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>파일업로드</title>
</head>

<body>
	<div th:if="${msg!=null}" style="">
		<h1>파일 업로드 결과</h1>
		<h2 th:text="${msg}">${msg}</h2>
	</div>

	<h1>단일 파일 보내기</h1>
	<form method="post" enctype="multipart/form-data" action="" th:action="@{fileUpload}">
		<label>file : </label> <input type="file" name="file"> <label>name
			: </label> <input type="text" name="name"> <input type="submit" value="upload">
	</form>

	<h2>여러개 파일 보내기</h2>
	<form method="post" enctype='multipart/form-data' action="" th:action="@{multiFileUpload}">
		<label>files : </label> <input type="file" name="files" multiple>
		<label>name : </label> <input type="text" name="name"> <input type="submit" value="upload">
	</form>

    <div id="root">
        
    </div>
    <!-- <script src="../resources/dist/app.js"></script> -->
    <script src="../dist/app.js"></script>
</body>

</html>
블로그 이미지

미나미나미

,

1. 파일 업로드  Rest Controller 만들기 

import java.io.File;
import java.io.IOException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.text.DateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import com.google.gson.Gson;

@RestController
@RequestMapping("restfiles")
public class RestFilesController {

	@Value("${file.path}")
	private String file_Path;

	private static final Logger logger = LoggerFactory.getLogger(RestFilesController.class);
	
	// 단일 파일 업로드 
	@PostMapping("/fileUpload")
	public String fileUpload(@RequestParam(required = false) MultipartFile file,
			@RequestParam(defaultValue = "test") String name) {
		if (file != null) {
			System.out.println("file.getSize() = "+ file.getSize());
			
			String msg = file.getOriginalFilename() + "is upload";
			
			// File.seperator 는 OS종속적이다.
			// Spring에서 제공하는 cleanPath()를 통해서 ../ 내부 점들에 대해서 사용을 억제한다
			Path copyOfLocation = Paths
					.get(file_Path + File.separator + StringUtils.cleanPath(file.getOriginalFilename()));
			try {
				// inputStream을 가져와서
				// copyOfLocation (저장위치)로 파일을 쓴다.
				// copy의 옵션은 기존에 존재하면 REPLACE(대체한다), 오버라이딩 한다
				Files.copy(file.getInputStream(), copyOfLocation, StandardCopyOption.REPLACE_EXISTING);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return "upload is complete";
	}
	
	
	// 멀티 파일 업로드 
	@PostMapping("/multiFileUpload")
	public String multiFileUpload(
			@RequestParam(required = false) MultipartFile[] files,
			@RequestParam(defaultValue = "test") String name) {
		
		HashMap<String, String> result = new HashMap<String, String>();
		Gson gson = new Gson();
		
		if (files != null) {
			for(MultipartFile file : files) {
				String msg = file.getOriginalFilename() + "is upload";
				System.out.println("name = " + name);
				System.out.println("msg = " + msg);

				// File.seperator 는 OS종속적이다.
				// Spring에서 제공하는 cleanPath()를 통해서 ../ 내부 점들에 대해서 사용을 억제한다
				Path copyOfLocation = Paths
						.get(file_Path + File.separator + StringUtils.cleanPath(file.getOriginalFilename()));
				try {
					// inputStream을 가져와서
					// copyOfLocation (저장위치)로 파일을 쓴다.
					// copy의 옵션은 기존에 존재하면 REPLACE(대체한다), 오버라이딩 한다
					Files.copy(file.getInputStream(), copyOfLocation, StandardCopyOption.REPLACE_EXISTING);
					result.put(file.getOriginalFilename(), "completed");
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return gson.toJson(result);
	}
}

 


 

 

2. 파일 업로드 Postman으로 테스트 하기

    # 단일 파일 업로드 

 

 

     #  멀티 파일 업로드 

블로그 이미지

미나미나미

,

1. 파일 업로드 페이지 (# fileForm.html)

<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<!DOCTYPE html>

<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">

<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>파일업로드</title>
</head>

<body>
	
	<div th:if="${msg!=null}" style="${msg!=null ? 'display:block' : 'display:none'}" >
		<h1>파일 업로드 결과</h1>
        <h2 th:text="${msg}">${msg}</h2>
    </div>
    
    <h1>단일 파일 보내기</h1>
    <form method="post" enctype="multipart/form-data" action="./fileUpload" th:action="@{/fileUpload}">
        <label>file : </label>
        <input type="file" name="file">
        <label>name : </label>
        <input type="text" name="name">
        <input type="submit" value="upload">
    </form>
    
    <h2>여러개 파일 보내기</h2>
    <form method="post"  enctype='multipart/form-data' action="./multiFileUpload" th:action="@{/multiFileUpload}">
        <label>files : </label>
        <input type="file" name="files" multiple>
        <label>name : </label>
        <input type="text" name="name">
        <input type="submit" value="upload">
    </form>
    
</body>
</html>

 

 

2.  파일 업로드 페이지 결과

   # 단일 파일 업로드 

 

 

   # 멀티 파일 업로드 


 

블로그 이미지

미나미나미

,

1. 파일 저장 위치 지정  (# application.config)

 

## FilePath 파일 저장 위치 
file.path=C:\\TestDir

 

 

2. 파일 컨트롤러 

import java.io.File;
import java.io.IOException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.text.DateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import com.google.gson.Gson;

@Controller
@RequestMapping("files")
public class FilesController {
	
	// 파일 저장할 위치
	@Value("${file.path}") 
	private String file_Path;

	private static final Logger logger = LoggerFactory.getLogger(FilesController.class);

	// 파일 폼 매핑 
	@GetMapping("/fileForm")
	public String fileForm(Locale locale, Model model) {
		// fileForm.html을 불러옴 
		return "fileForm";
	}
	
	// 파일 등록 매핑 
    @PostMapping("/fileUpload")
    public String fileUpload(
    		@RequestParam(required=false) MultipartFile file, 
    		@RequestParam(defaultValue = "test")String name,
    		RedirectAttributes attributes){
    	
    	String msg = file.getOriginalFilename() + "is upload";
        attributes.addFlashAttribute("msg" , msg);
        
        // File.seperator 는 OS종속적이다.
        // Spring에서 제공하는 cleanPath()를 통해서 ../ 내부 점들에 대해서 사용을 억제한다
        Path copyOfLocation = Paths.get(file_Path + File.separator + StringUtils.cleanPath(file.getOriginalFilename()));
        try {
            // inputStream을 가져와서
            // copyOfLocation (저장위치)로 파일을 쓴다.
            // copy의 옵션은 기존에 존재하면 REPLACE(대체한다), 오버라이딩 한다
            Files.copy(file.getInputStream(), copyOfLocation, StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return "redirect:/files/fileForm";
    }
    
    
    // 여러 개의 파일 업로드 
    @PostMapping("/multiFileUpload")
	public String multiFileUpload(
			@RequestParam(required = false) MultipartFile[] files,
			@RequestParam(defaultValue = "test") String name,
    		RedirectAttributes attributes){
		
    	// 결과값 리턴 
    	HashMap<String, String> result = new HashMap<String, String>();
		Gson gson = new Gson();
		
		// 파일 있을 경우만 
		if (files != null) {
			for(MultipartFile file : files) {
				Path copyOfLocation = Paths
						.get(file_Path + File.separator + StringUtils.cleanPath(file.getOriginalFilename()));
				try {
					Files.copy(file.getInputStream(), copyOfLocation, StandardCopyOption.REPLACE_EXISTING);
					result.put(file.getOriginalFilename(), "completed");
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		// 파일 업로드 결과
		attributes.addFlashAttribute("msg" , gson.toJson(result));
		return "redirect:/files/fileForm";
	}
}

 

블로그 이미지

미나미나미

,