티스토리 뷰

 

리액트와 스프링프레임워크 연동 / 게시판 만들기

 

1. 리액트와 스프링프레임워크 연동

(1) 프로젝트 생성

-이름 : reactServer

-gradle project / Jar / 11 / wsy

-dependecies

Spring Boot DevTools Spring Configuration Processor Spring Web Lombok
thymeleaf MyBatis Framework MySQL Driver  

 

 

(2) 디비 연동 끄기

-디비 연동 안 되어 있기 때문에.. 꺼줘야 지금 에러 안 뜸.

(2-1) Gadle에서 mabatis랑 mysql 주석 처리

(2-2) 프로젝트 우클릭 -> 그래들 -> refresh Gradle

 

>> 포트 번호 변경_application.properties

server.port=9090

 

>> 프로젝트의 디폴트 패키지에서 새로운 패키지 생성 : controller

package com.wsy.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class TestController {
	
	@RequestMapping("/")
	public String index() throws Exception {
		return "index";
	}

}

 

>> template 우클릭 -> New -> Other -> web : HTML 파일 : index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1>React와 연동할 서버 테스트</h1>
</body>
</html>

 

>> 프로젝트 실행했을 때, 제대로 뜨는지 확인 : 호출 ↓

http://localhost:9090/

 

(3) ajax 통신을 위한 처리

(3-1) index.html

-jQuery 코드 가져오기 및 ajax 통신을 위한 코드 추가 : js 코드, 버튼 추가

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<!-- jqeury google cdn -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>

<script type="text/javascript">
	$(document).ready(function(){
		$("#btn-click").on("click", function(){
			alert("클릭");
		});
		
		//ajax 통신 
		$("#btn-ajax").on("click", function(){
			var datas = {data :$("#input-text").val()};
			
			$.ajax({
				url: "/ajax/test",
				type: "post",
				data: datas,
				success: function(res){
					alert("통신 성공 \n"+res);
				},
				error: function(err){
					alert("에러가 발생했습니다 \n"+err);
				}
			});
		});
	});
</script>

</head>
<body>
	<h1>React와 연동할 서버 테스트</h1>
	<label for="input-text">전송할 데이터 : </label>
	<input type="text" id="input-text"><br><br>
	<button type="button" id="btn-click">클릭</button>
	<!-- ajax 통신 버튼 -->
	<button type="button" id="btn-ajax">서버와 ajax 통신</button>
</body>
</html>

 

(3-2) TestController

package com.wsy.demo.controller;

import org.springframework.stereotype.Controller;
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.ResponseBody;

@Controller
public class TestController {
	
	@RequestMapping("/")
	public String index() throws Exception {
		return "index";
	}
	
	//ajax 통신을 위한 컨트롤러
	@ResponseBody
	@RequestMapping(value="/ajax/test", method=RequestMethod.POST)
	public Object ajaxTest(@RequestParam ("data") String data) {
		System.out.println("서버로 전송된 데이터 : "+data);
		return data;
	}

}

→ 버튼 클릭했을 때, 버튼 정상적 작동, 데이터값 제대로 가져오고 있음

 

(4) 리액트 프로젝트로 axios 통신

(4-1) 리액트 프로젝트 생성

-VSCODE 터미널에서

>>yarn create react-app react-bbs
>>cd react-bbs
>>yarn start

>> 웹 페이지 제대로 뜨는지 확인

 

(4-2) axios 설치

-해당 프로젝트에서 

>>yarn add axios

>>0.27.2 버전 설치

>>yarn start

>> 제대로 동작하는지 확인만

 

(4-3) 비주얼스튜디오

-STS에서 해도 되는데 비주얼스튜디오가 훨씬 편할 것임.

-폴더 열기 : react-bbs

 

(+) 깃허브 연동

깃허브 사이트 -> 레파지토리 생성 : react-bbs -> 주소 복사

▶ 소스트리 오픈 -> [ + ] -> Add : react-bbs 추가 -> 올리고 설명 "axios 모듈 추가" 후 커밋 ->

-> 원격 추가 : 이름  깃허브 토큰 / 

-> 푸시(push) : 마스터 체크

 

(+) prettierrc 파일 추가

react-bbs 폴더에 prettierrc 파일 추가

 

(4-4) src / axiosTest.js 생성

import React from 'react';

class AxiosTest extends React.Component {
	render(){
        return(
            <div>
                React 화면 테스트
            </div>
        );
    }
}
export default AxiosTest;

 

(4-5) App.js

-안 쓰는 거 삭제 / AxiosTest 출력 및 import

// import logo from './logo.svg';
import './App.css';
import AxiosTest from './axiosTest';

function App() {
  return (
    <div className="App">
      <AxiosTest/>
    </div>
  );
}

export default App;

 

>>axiosTest.js 추가 및 수정

import React from 'react';
import axios from 'axios';

class AxiosTest extends React.Component {
    getData = () => {
        axios
        .get('http://localhost:9090/ajax/test')//STS에서 만들어둔 주소.
        .then((res) =>{
            console.log('통신성공');
            console.log(res);
        })
        .catch((err) => {
            console.log('통신실패');
            console.log(err);
        });
    };
	render(){
        return(
            <div>
                <h1>React 화면 테스트</h1>
                <button type='button' onClick={this.getData}>
                    클릭 시 서버와 통신
                </button>
            </div>
        );
    }
}
export default AxiosTest;

└서버 돌고 있음. 리액트랑, STS.

근데 웹브라우저에서 [f12]눌러서 확인해보면. 통신 실패로 뜨는데 지금 상태에서는 에러 뜨는 게 맞음.

 

※ CORS(Cross-Origin Resource Sharing)

http 헤더를 사용하여 한 출처에서 실행중인 웹 애플리케이션이 다른 출처의 자원에 접근할 경우, 오류 발생

→ 리액트 프로젝트의 package.json 파일에 "proxy" : "서버 주소" 추가

→ 스프링서버에서 처리

    └> @CorsOrigin 어노테이션 사용 : Controller 파일에 설정

    └> WebMvcConfigurer에서 설정 : 이 파일을 새로 생성해서 설정해야 함.

 

(4-6) CORS 오류 처리 방법 - proxy

-STS에서 통신 방식 변경 - get

>>TestController

//ajax 통신을 위한 컨트롤러
@ResponseBody
@RequestMapping(value="/ajax/test", method=RequestMethod.GET) //get으로 수정
public Object ajaxTest(@RequestParam ("data") String data) {
    System.out.println("서버로 전송된 데이터 : "+data);
    return data;
}

>>index.js

//ajax 통신 
$("#btn-ajax").on("click", function(){
    var datas = {data :$("#input-text").val()};

    $.ajax({
        url: "/ajax/test",
        type: "get", //get으로 변경
        data: datas,
        success: function(res){
            alert("통신 성공 \n"+res);
        },
        error: function(err){
            alert("에러가 발생했습니다 \n"+err);
        }
    });
});

 

-VSCODE에서 App.js

데이터 실어 보내려고 임시로 쿼리스트링 적음.

import React from 'react';
import axios from 'axios';

class AxiosTest extends React.Component {
    getData = () => {
        axios
        .get('http://localhost:9090/ajax/test?data=test')//더미 데이터 보내려고 쿼리스트링추가
        .then((res) =>{
            console.log('통신성공');
            console.log(res);
        })
        .catch((err) => {
            console.log('통신실패');
            console.log(err);
        });
    };
	render(){
        return(
            <div>
                <h1>React 화면 테스트</h1>
                <button type='button' onClick={this.getData}>
                    클릭 시 서버와 통신
                </button>
            </div>
        );
    }
}
export default AxiosTest;

 

-프로젝트 실행 후 웹브라우저의 버튼을 클릭했을 때, 에러 발생 : http의 보안때문에 발생한 에러

 리소스 요청 시 , 서버의 주소와 요청하는 곳의 주소가 같아야 하는데 현 상태가 다르기 때문에 에러.

Access to XMLHttpRequest at 'http://localhost:9090/ajax/test?getDate=data' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

※ CORS(Cross-Origin Resource Sharing)

http 헤더를 사용하여 한 출처에서 실행중인 웹 애플리케이션이 다른 출처의 자원에 접근할 경우, 오류 발생

 

(현재) 스프링부트 로컬호스트 9090 / 리액트 로컬호스트 3000라서 에러

>>일단 영화 앱에서 데이터 가져오는 거 되는 지 확인. 웹사이트 주소라서 에러 안 뜰 것임.

 

-axiosTest.js → 코드 수정 후 웹브라우저 실행했을 때, 버튼 클릭 시 영화 데이터 가져옴.

import React from 'react';
import axios from 'axios';

class AxiosTest extends React.Component {
    getData = () => {
        axios
        // .get('http://localhost:9090/ajax/test?data=test')//STS에서 만들어둔 주소.
        //영화사이트에서 데이터 가져오기
        .get('http://yts-proxy.now.sh/list_movies.json') //통신성공
        .then((res) =>{
            console.log('통신성공');
            console.log(res);
        })
        .catch((err) => {
            console.log('통신실패');
            console.log(err);
        });
    };
	render(){
        return(
            <div>
                <h1>React 화면 테스트</h1>
                <button type='button' onClick={this.getData}>
                    클릭 시 서버와 통신
                </button>
            </div>
        );
    }
}
export default AxiosTest;

 

-package.json : proxy 추가

 먼저, 리액트 실행 중인 거 종료. [ ctrl ] + [ v ] 여러 번 눌러서.  → proxy 코드 추가

{
  "name": "react-bbs",
  "version": "0.1.0",
  "private": true,
  "proxy":"http://localhost:9090", //★ 이거 추가
  "dependencies": {
    "@testing-library/jest-dom": "^5.14.1",
    "@testing-library/react": "^13.0.0",
    "@testing-library/user-event": "^13.2.1",
    "axios": "^0.27.2",
    "react": "^18.1.0",
    "react-dom": "^18.1.0",
    "react-scripts": "5.0.1",
    "web-vitals": "^2.1.0"
  },
  ...

 

-axiosTest.js

import React from 'react';
import axios from 'axios';

class AxiosTest extends React.Component {
    getData = () => {
        axios
        // .get('http://localhost:9090/ajax/test?data=test')//STS에서 만들어둔 주소.
        // .get('http://yts-proxy.now.sh/list_movies.json') //통신성공
        //package.json에서 proxy 추가 후
        .get('/ajax/test?data=test') //데이터 보내기
        .then((res) =>{
            console.log('통신성공');
            console.log(res);
        })
        .catch((err) => {
            console.log('통신실패');
            console.log(err);
        });
    };
	render(){
        return(
            <div>
                <h1>React 화면 테스트</h1>
                <button type='button' onClick={this.getData}>
                    클릭 시 서버와 통신
                </button>
            </div>
        );
    }
}
export default AxiosTest;

 

>> 웹브라우저에서 데이터 제대로 가져오는지 확인

(4-6) CORS 오류 처리 방법 - Srping

-기존에 설정해두었던 VSCODE의 package.json에서 프록시 주소 변경 또는 삭제

//"proxy":"http://localhost:9090",

"proxy":"http://localhost:8085",

 

-AxiosTest.js

import React from 'react';
import axios from 'axios';

class AxiosTest extends React.Component {
    getData = () => {
        axios
        .get('http://localhost:9090/ajax/test?data=test')//STS에서 만들어둔 주소.
        .then((res) =>{
            console.log('통신성공');
            console.log(res);
        })
        .catch((err) => {
            console.log('통신실패');
            console.log(err);
        });
    };
	render(){
        return(
            <div>
                <h1>React 화면 테스트</h1>
                <button type='button' onClick={this.getData}>
                    클릭 시 서버와 통신
                </button>
            </div>
        );
    }
}
export default AxiosTest;

>> 실행해서 버튼 클릭했을 때, 에러 뜨는 상황인 것 확인

 

-방법1) STS의 TestController : 어노테이션 추가

package com.wsy.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
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.ResponseBody;

//@CrossOrigin(origins = "http://localhost:3000/")
@Controller
public class TestController {
	
	@RequestMapping("/")
	public String index() throws Exception {
		return "index";
	}
	
	//ajax 통신을 위한 컨트롤러
	@CrossOrigin(origins = "http://localhost:3000/")
	@ResponseBody
	@RequestMapping(value="/ajax/test", method=RequestMethod.GET)
	public Object ajaxTest(@RequestParam ("data") String data) {
		System.out.println("서버로 전송된 데이터 : "+data);
		return data;
	}

}

 

-방법 2) WebMvc _ Error 상태

spring Framework의 webMvcConfigurer 인터페이스 상속받아 오버라이딩 하는 방식

WebMvcConfiguere를 상속받는 클래스에 @Configuration 어노테이션 사용

addMapping("적용할 주소") : 매개변수로 스프링 서버에서 적용될 주소 입력, 컨트롤러에서 사용자 입력을 받기 위한 주소

allowedOrigins("접속하는 외부서버주소") : 현재 스프링 서버로 접속할 외부 주소 입력

>>패키지 생성 : configuration / 클래스 생성 WebMvcConfigurer

package com.wsy.demo.configuration;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer{
	
	@Override
	public void addCorsMapping(CorsRegistry registry) { //Error : @Override 지우라고 함...
		registry.addMapping("/**")
		.allowedOrigins("http://localhost:3000/");
	}
}

└오버라이드 어노테이션 지우라는 에러떠서 지우고 하면 안 됨... 통신 에러..

 

>> VSCODE에서 Proxy 설정하는 걸로 돌림.

 

(5) 디비연결

(5-1) 디비 연결 : sql

-mysql에서 스키마 생성 : react_board_db

-테이블 생성 : board

 

(5-2) 디비 연결 : STS

-application.properties에서 디비 연결

server.port=9090

#DB connect setting : oracle or mysql
spring.datasource.hikari.driver-class-name=com.mysql.cj.jdbc.Driver

#DB 접속 주소, 사용옵션 / 3306은 mysql 주소
spring.datasource.hikari.jdbc-url=jdbc:mysql://localhost:3306/react_board_db?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
#
spring.datasource.hikari.username=wsy
spring.datasource.hikari.password=1234
spring.datasource.hikari.connection-test-query=SELECT 1

#mybatis 사용 시 카멜케이스 적용
mybatis.configuration.map-underscore-to-camel-case=true

#rest api 사용 시, GET / POST / PUT / DELETE 옵션 사용하도록 설정
spring.mvc.hiddenmethod.filter.enabled=true

 

(+) 디비 설정할 거기 때문에 그래들에서 주석 처리 해둔 것 주석 해제 → 프로젝트 우클릭 : Gradle Refresh

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.2'
	compileOnly 'org.projectlombok:lombok'
	developmentOnly 'org.springframework.boot:spring-boot-devtools'
	runtimeOnly 'mysql:mysql-connector-java'
	annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

 

>>configuration 패키지에 클래스 생성 : DatabaseConfiguration

package com.wsy.demo.configuration;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

@Configuration
@PropertySource("classpath:/application.properties")
public class DatabaseConfiguration {
	
	@Autowired
	private ApplicationContext appContext;
	
	//database setting
	@Bean
	@ConfigurationProperties(prefix="spring.datasource.hikari")
	public HikariConfig hikariConfig() {
		return new HikariConfig();
	}

	@Bean
	public DataSource dataSource() throws Exception{
		DataSource dataSource = new HikariDataSource(hikariConfig());
		System.out.println(dataSource.toString());
		return dataSource;
	}
	
	//sql session factory
	@Bean
	public SqlSessionFactory sqlSessionFactory(DataSource ds) throws Exception{
		SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
		ssfb.setDataSource(ds);
		ssfb.setMapperLocations(appContext.getResources("classpath:/sql/**/sql-*.xml"));
		ssfb.setConfiguration(mybatisConfig());
		return ssfb.getObject();
	}
	
	@Bean
	public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory ssf) {
		return new SqlSessionTemplate(ssf);
	}
	
	//mybatis
	@Bean
	@ConfigurationProperties(prefix="mybatis.configuration")
	public org.apache.ibatis.session.Configuration mybatisConfig(){
		return new org.apache.ibatis.session.Configuration();
	}
}

 

>>src/main/resource -> sql 폴더 생성

xml 파일 생성 : sql-board.xml > 열 때, 제네릭으로 열기

(파일이름이 sql의 이름과 같아야 함)

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wsy.demo.BoardMapper"></mapper>

(*폴더 위치 중요)

>> 로컬호스트 호출했을 때, 웹브라우저 정상적으로 연결되는지 확인

http://localhost:9090/

 

>> 기본 디비 설정 완료 <<

컨트롤러에서 요청을 하면 -> Service에서 받아서 mapper랑 연결해야 함. ->

DTO 파일도 만들어야 함.

(5-3) DTO 생성

-패키지 dto 생성 - 클랫 생성 : BoardDto

package com.wsy.demo.dto;

import lombok.Data;

@Data
public class BoardDto {
	
	private int boardNo;
	private String title;
	private String content;
	private String createId;
	private String createDate;
	private String updateId;
	private String updateDate;
	private int hitCnt;

}

 

(+) 실제로 데이터 가져오는지 아닌지 체크하는 부분

-TestController.java

package com.wsy.demo.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
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.ResponseBody;

import com.wsy.demo.dto.BoardDto;

//@CrossOrigin(origins = "http://localhost:3000/")
@Controller
public class TestController {
	//★
	@Autowired
	private BoardService boardService;
	
	@RequestMapping("/")
	public String index() throws Exception {
		return "index";
	}
	
	//ajax 통신을 위한 컨트롤러
//	@CrossOrigin(origins = "http://localhost:3000/")
	@ResponseBody
	@RequestMapping(value="/ajax/test", method=RequestMethod.GET)
	public Object ajaxTest(@RequestParam ("data") String data) {
		System.out.println("서버로 전송된 데이터 : "+data);
		return data;
	}
	
    //★
	@ResponseBody
	@RequestMapping(value="/ajax/boardList", method=RequestMethod.GET)
	public Object ajaxBoardList() throws Exception{
		List<BoardDto> dataList = boardService.selectBoardList();
		return dataList;
	}
}

└@Autowired 마우스 커서 갖다대면 create 뜨는데 인터페이스 선택. 이후 자동 완성으로 Service, mapper 생성

 

-sql-board.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wsy.demo.mapper.BoardMapper">

	<select id="selectBoardList" resultType="com.wsy.demo.dto.BoardDto">
		<![CDATA[
		SELECT
			board_no,
			title,
			create_id,
			DATE_FORMAT(create_date, '%Y.%m.%d %H:%i:%s') AS create_date,
			hit_cnt
		FROM
			board
		WHERE
			delete_yn = 'N'
		ORDER BY board_no DESC
		]]>
	</select>
</mapper>

(5-4) Service 생성

package com.wsy.demo.service;

import java.util.List;

import com.wsy.demo.dto.BoardDto;

public interface BoardService {

	List<BoardDto> selectBoardList() throws Exception;

}
package com.wsy.demo.service;

import java.util.List;

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

import com.wsy.demo.dto.BoardDto;
import com.wsy.demo.mapper.BoardMapper;

@Service
public class BoardServiceImpl implements BoardService {
	
	@Autowired
	private BoardMapper boardMapper;

	@Override
	public List<BoardDto> selectBoardList() throws Exception {
		return boardMapper.selectBoardList();
	}

}

(5-6) mapper 생성

package com.wsy.demo.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;

import com.wsy.demo.dto.BoardDto;

@Mapper
public interface BoardMapper {
	
	List<BoardDto> selectBoardList() throws Exception;

}

 

>> index.html tnwjd

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<!-- jqeury google cdn -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>

<script type="text/javascript">
	$(document).ready(function(){
...
		$("#btn-board-list").on("click", function(){
			$.ajax({
				url: "/ajax/boardList",
				type: "get",
				success: function(res){
					alert("통신 성공 \n"+res);
					console.log(res);
				},
				error: function(err){
					alert("에러가 발생했습니다 \n"+err);
				}
			});
		});
	});
</script>

</head>
<body>
	<h1>React와 연동할 서버 테스트</h1>
...
	<button type="button" id="btn-board-list">board BTN</button>
</body>
</html>

 

>> 실행 : 웹브라우저

 

(5-7) VSCODE와 연동되는지 확인

import React from 'react';
import axios from 'axios';

class AxiosTest extends React.Component {
    getData = () => {
        axios
        .get('/ajax/boardList') //spring 연동
        .then((res) =>{
            console.log('통신성공');
            console.log(res);
        })
        .catch((err) => {
            console.log('통신실패');
            console.log(err);
        });
    };
	render(){
        return(
            <div>
                <h1>React 화면 테스트</h1>
                <button type='button' onClick={this.getData}>
                    클릭 시 서버와 통신
                </button>
            </div>
        );
    }
}
export default AxiosTest;

 

>> 버튼 눌렀을 때, 데이터 가져오는지 확인(스프링부트 서버 시작, 리액트 서버 돌아가는 상태여야 함)


3. 실습 : 

문제 1) BoardService, BoardServiceImpl, BoardMapper, sql-board.xml에

글 읽기, 글 쓰기, 글 수정, 글 삭제 기능 구현하기

 

(1) mysql 쿼리문 작성

SELECT * FROM react_board_db.board;

-- 전체 리스트
SELECT
			board_no,
			title,
			create_id,
			DATE_FORMAT(create_date, '%Y.%m.%d %H:%i:%s') AS create_date,
			hit_cnt
		FROM
			board
		WHERE
			delete_yn = 'N'
		ORDER BY board_no DESC;

-- 수정한 내용 조회
SELECT
	board_no,
	title,
    create_id,
    create_date,
	update_id,
	update_date,
	hit_cnt,
    delete_yn
FROM
	board
WHERE
	delete_yn = 'N'
ORDER BY board_no DESC;

-- 상세글 확인
SELECT board_no, title, content, create_id, create_date, hit_cnt
FROM board
WHERE delete_yn = 'N' AND board_no = 1;

-- 상세글 확인 시 조회수(hit_cnt) 변경
UPDATE board
SET hit_cnt = hit_cnt + 1
WHERE board_no = 1;

-- 글 쓰기
INSERT INTO board(title, content, create_id, create_date)
VALUES ('test3', 'content_test3', 'wsy', NOW());

-- 글 수정 : delete_yn 필요없음
UPDATE board
SET title = 'Edit_test', content = 'Edit_content', update_id = 'yun', update_date = now()
WHERE board_no = 1;

-- 글 삭제 : Update로 삭제 처리
-- SET 부분에 update_id = 'admin' 추가해도 되고 아니고
UPDATE board
SET delete_yn = 'Y', update_date = NOW()
WHERE board_no = 1;

 

(1-1) spring : xml 코드

 

(2) 인터페이스 BoardService

package com.wsy.demo.service;

import java.util.List;

import com.wsy.demo.dto.BoardDto;

public interface BoardService {

//  전체글
	List<BoardDto> selectBoardList() throws Exception;
	
//	상세글 확인
	public BoardDto selectBoardDetail(int boardNo) throws Exception;
	
//	글 쓰기
	public void insertBoard(BoardDto board) throws Exception;
	
//	글 수정
	public void updateBoard(BoardDto board)  throws Exception;
	
	public void deleteBoard(int boardNo)  throws Exception;
	
//	public int hitCnt(int boardNo);

}

(2-1) boardServiceImple

package com.wsy.demo.service;

import java.util.List;

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

import com.wsy.demo.dto.BoardDto;
import com.wsy.demo.mapper.BoardMapper;

@Service
public class BoardServiceImpl implements BoardService {
	
	@Autowired
	private BoardMapper boardMapper;

	@Override
	public List<BoardDto> selectBoardList() throws Exception {
		return boardMapper.selectBoardList();
	}

	@Override
	public BoardDto selectBoardDetail(int boardNo) throws Exception {
		boardMapper.updateHitCount(boardNo);
		return boardMapper.selectBoardDetail(boardNo);
	}

	@Override
	public void insertBoard(BoardDto board) throws Exception {
		boardMapper.insertBoard(board);
		
	}

	@Override
	public void updateBoard(BoardDto board) throws Exception {
		boardMapper.updateBoard(board);
		
	}

	@Override
	public void deleteBoard(int boardNo) throws Exception {
		boardMapper.deleteBoard(boardNo);		
	}


}

(2-3) boardMapper

package com.wsy.demo.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import com.wsy.demo.dto.BoardDto;

@Mapper
public interface BoardMapper {
	
	List<BoardDto> selectBoardList() throws Exception;
	
//	public void insert(BoardDto dto);
//	public BoardDto read(int boardNo);
//	public boolean update(BoardDto dto);
//	public boolean delete(int boardNo);
//	public int hitCnt(int boardNo);

	void updateHitCount(@Param("boardNo")int boardNo) throws Exception;

	BoardDto selectBoardDetail(@Param("boardNo") int boardNo) throws Exception;

	void insertBoard(BoardDto board) throws Exception;

	void updateBoard(BoardDto board) throws Exception;

	void deleteBoard(@Param("boardNo") int boardNo) throws Exception;

}

 

(2-4) xml 쿼리문

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wsy.demo.mapper.BoardMapper">

<!--기본 타입(Int, ..)아닌 것은 mysql이 데이터타입을 모르기 때문에 정확히 명시해줘야 함.-->
	<select id="selectBoardList" resultType="com.wsy.demo.dto.BoardDto">
		<![CDATA[
		SELECT
			board_no,
			title,
			create_id,
			DATE_FORMAT(create_date, '%Y.%m.%d %H:%i:%s') AS create_date,
			hit_cnt
		FROM
			board
		WHERE
			delete_yn = 'N'
		ORDER BY board_no DESC
		]]>
	</select>

	<select id="read" resultType="com.wsy.demo.dto.BoardDto">
		<![CDATA[
			SELECT board_no, title, content, create_id, create_date, hit_cnt
			FROM board
			WHERE delete_yn = 'N' AND board_no = ${boardNo}
		]]>
	</select>
	
	<select id="selectBoardDetail" parameterType="int" resultType="com.wsy.demo.dto.BoardDto">
		<![CDATA[ 
			SELECT board_no, title, content, create_id, create_date, hit_cnt
			FROM board
			WHERE delete_yn = 'N' AND board_no = ${boardNo}
		]]>
	</select>

	<!-- 조회수-->
	<update id="updateHitCount" parameterType="int">
		<![CDATA[
			UPDATE board
			SET hit_cnt = hit_cnt + 1
			WHERE board_no =${boardNo}
		]]>
	</update>
	
	<insert id="insertBoard" parameterType="com.wsy.demo.dto.BoardDto">
		<![CDATA[ 
			INSERT INTO board(title, content, create_id, create_date)
			VALUES (#{title}, #{content}, #{createId}, NOW())
		]]>
	</insert>
	
	<update id="updateBoard" parameterType="com.wsy.demo.dto.BoardDto">
		<![CDATA[ 
			UPDATE board
			SET title = #{title}, 
				content = #{title}, 
				update_id = #{updateId}, 
				update_date = now()
			WHERE board_no = #{boardNo}
		]]>
	</update>
	
	<update id="deleteBoard" parameterType="int">
		<![CDATA[ 
			UPDATE board
			SET delete_yn = 'Y', update_date = NOW()
			WHERE board_no = #{boardNo}
		]]>
	</update>

</mapper>

 

>> 데이터 넘어오는지 확인하기 위해서

(3) Controller

package com.wsy.demo.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
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.ResponseBody;

import com.wsy.demo.dto.BoardDto;
import com.wsy.demo.service.BoardService;

//@CrossOrigin(origins = "http://localhost:3000/")
@Controller
public class TestController {
	
	@Autowired
	private BoardService boardService;
	
	@RequestMapping("/")
	public String index() throws Exception {
		return "index";
	}
	
//  ajax 통신을 위한 컨트롤러
//	@CrossOrigin(origins = "http://localhost:3000/")
	@ResponseBody
	@RequestMapping(value="/ajax/test", method=RequestMethod.GET)
	public Object ajaxTest(@RequestParam ("data") String data) {
		System.out.println("서버로 전송된 데이터 : "+data);
		return data;
	}

	@ResponseBody
	@RequestMapping(value="/ajax/boardList", method=RequestMethod.GET)
	public Object ajaxBoardList() throws Exception{
		List<BoardDto> dataList = boardService.selectBoardList();
		return dataList;
	}
//연결
//상세글 확인
	@ResponseBody
	@RequestMapping(value="/ajax/boardDetail/{boardNo}", method=RequestMethod.GET)
	public Object ajaxBoardDetail(@PathVariable("boardNo") int boardNo) throws Exception {
		BoardDto data = boardService.selectBoardDetail(boardNo);
		return data;
	}
//글 쓰기
	@ResponseBody
	@RequestMapping(value="/ajax/boardWrite", method=RequestMethod.POST)
	public Object ajaxBoardInsert(BoardDto board) throws Exception {
		boardService.insertBoard(board);
		return "success";
		//원래는 if문을 써서 해당값이 들어갔는지 확인하는 작업이 필요한데..간단하게 하는 거라 생략
	}
//글 수정
	@ResponseBody
	@RequestMapping(value="/ajax/boardUpdate/{boardNo}", method = RequestMethod.PUT)
	public Object ajaxBoardUpdate(BoardDto board) throws Exception {
		boardService.updateBoard(board);
		return "success";
	}
//글 삭제
	@ResponseBody
	@RequestMapping(value="/ajax/boardDelete/{boardNo}", method = RequestMethod.DELETE)
	public Object ajaxBoardDelete(@PathVariable("boardNo") int boardNo) throws Exception {
		boardService.deleteBoard(boardNo);
		return "success";
	}
}

 

(4) index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<!-- jqeury google cdn -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>

<script type="text/javascript">
	$(document).ready(function(){
		$("#btn-click").on("click", function(){
			alert("클릭");
		});
		
		//ajax 통신 
		$("#btn-ajax").on("click", function(){
			var datas = {data :$("#input-text").val()};
			
			$.ajax({
				url: "/ajax/test",
				type: "get",
				data: datas,
				success: function(res){
					alert("통신 성공 \n"+res);
				},
				error: function(err){
					alert("에러가 발생했습니다 \n"+err);
				}
			});
		});
		$("#btn-board-list").on("click", function(){
			$.ajax({
				url: "/ajax/boardList",
				type: "get",
				success: function(res){
					alert("통신 성공 \n"+res);
					console.log(res);
				},
				error: function(err){
					alert("에러가 발생했습니다 \n"+err);
				}
			});
		});
		//
		$("#btn-detail").on("click", function(){
			$.ajax({
				url: "/ajax/boardDetail/1",
				type: "GET",
				success: function(res){
					alert("통신 성공_detail"+res);
				},
				error: function(err){
					alert("통신 오류_detail"+err);
				}
			});
		});
		//
		$("#btn-write").on("click", function(){
			var datas = {title:$("#input-title").val(), content:$("#input-content").val(), createId:$("#input-user-id").val()};
			$.ajax({
				url: "/ajax/boardWrite",
				type: "POST",
				data: datas,
				success: function(res){
					alert("통신 성공_wri");
				},
				error: function(err){
					alert("통신 오류_wri");
				}
			});
		});//
		$("#btn-update").on("click", function(){
			var boardNo = $("#input-num").val()
			var datas = {title: $("#input-title").val(), content:$("#input-content").val(), update_id:$("#input-user-id").val()};
			$.ajax({
				url: "/ajax/boardUpdate/"+boardNo,
				type: "PUT",
				data: datas,
				success: function(res){
					alert("통신 성공_up");
				},
				error: function(err){
					alert("통신 오류_up");
				}
			})
		})//
		$("#btn-delete").on("click", function(){
			var boardNo = $("#input-num").val()
			$.ajax({
				url: "/ajax/boardDelete/"+boardNo,
				type: "DELETE",
				success: function(res){
					alert("통신 성공_del");
				},
				error: function(err){
					alert("통신 오류_del");
				}
			})
		})
	});
</script>

</head>
<body>
	<h1>React와 연동할 서버 테스트</h1>
	<label for="input-text">전송할 데이터 : </label>
	<input type="text" id="input-text"><br><br>
	<button type="button" id="btn-click">클릭</button>
	<!-- ajax 통신 버튼 -->
	<button type="button" id="btn-ajax">서버와 ajax 통신</button>
		<button type="button" id="btn-board-list">board BTN</button>
	<br><hr><br>
	<h1>Board</h1>
	<br>
	<label for="input-num">Board No : </label>
	<input type="text" id="input-num"><br>
	
	<label for="input-title">Board title : </label>
	<input type="text" id="input-title"><br>
	
	<label for="input-content">Content : </label>
	<input type="text" id="input-content"><br>
	
	<label for="input-user-id">User ID : </label>
	<input type="text" id="input-user-id"><br>
		
	<button type="button" id="btn-detail">Detail</button>
	<button type="button" id="btn-write">Write</button>
	<button type="button" id="btn-update">Update</button>
	<button type="button" id="btn-delete">Delete</button>
	<hr>
	
</body>
</html>

 

(5) ReactBoardController.java

기존의 testController의 내용 모두 주석 처리하고 복붙. responsebody만 빼고

package com.wsy.demo.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
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.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.wsy.demo.dto.BoardDto;
import com.wsy.demo.service.BoardService;

@RestController
public class ReactBoardController {
	
	@Autowired
	private BoardService boardService;
	
	@RequestMapping("/")
	public String index() throws Exception {
		return "index";
	}
	
//  ajax 통신을 위한 컨트롤러
//	@CrossOrigin(origins = "http://localhost:3000/")
	@RequestMapping(value="/ajax/test", method=RequestMethod.GET)
	public Object ajaxTest(@RequestParam ("data") String data) {
		System.out.println("서버로 전송된 데이터 : "+data);
		return data;
	}

	@RequestMapping(value="/ajax/boardList", method=RequestMethod.GET)
	public Object ajaxBoardList() throws Exception{
		List<BoardDto> dataList = boardService.selectBoardList();
		return dataList;
	}
//연결
//상세글 확인
	@RequestMapping(value="/ajax/boardDetail/{boardNo}", method=RequestMethod.GET)
	public Object ajaxBoardDetail(@PathVariable("boardNo") int boardNo) throws Exception {
		System.out.println(boardNo);
//		return "success";
		BoardDto data = boardService.selectBoardDetail(boardNo);
		return data;
	}
//글 쓰기
	@RequestMapping(value="/ajax/boardWrite", method=RequestMethod.POST)
	public Object ajaxBoardInsert(BoardDto board) throws Exception {
		System.out.println(board);
//		return "success";
//		boardService.insertBoard(board);
		return "success";
		//원래는 if문을 써서 해당값이 들어갔는지 확인하는 작업이 필요한데..간단하게 하는 거라 생략
	}
//글 수정
	@RequestMapping(value="/ajax/boardUpdate/{boardNo}", method = RequestMethod.PUT)
	public Object ajaxBoardUpdate(BoardDto board) throws Exception {
		
		boardService.updateBoard(board);
		System.out.println(board);
		return "success";
	}
//글 삭제
	@RequestMapping(value="/ajax/boardDelete/{boardNo}", method = RequestMethod.DELETE)
	public Object ajaxBoardDelete(@PathVariable("boardNo") int boardNo) throws Exception {
		boardService.deleteBoard(boardNo);
		return "success";
	}
	
}

 

-CMD : 리액트 라우터 돔 설치

yarn add react-router-dom@5.2.1

 

-App.js

exact={true} 넣어줘야 화면이 중첩되지 않음.

// import logo from './logo.svg';
import './App.css';
// import AxiosTest from './axiosTest';
import React from 'react';
import { HashRouter, Route } from 'react-router-dom';

import Home from './routes/Home';
import Detail from './routes/Detail';
import Write from './routes/Write';
import Navigation from './components/Navigation';

function App() {
  return (
    <HashRouter>
      <Navigation />
      <Route path="/" exact={true} component={Home} />
      <Route path="/detail" exact={true} component={Detail} />
      <Route path="/write" exact={true} component={Write} />
    </HashRouter>
    // <div className="App">
    //   <AxiosTest/>
    // </div>
  );
}

export default App;

 

-src : 폴더 생성 : components, routers

 

-Navigation.js

import React from 'react';
import { Link } from 'react-router-dom';

function Navigation() {
  return (
    <div className="nav">
      <Link to="/">Home</Link>
      <Link to="/write">Write</Link>
    </div>
  );
}

export default Navigation;

 

-routers 폴더 안에 Detail.js, Home.js, Write.js 생성

import React from 'react';

class Home extends React.Component {
  render() {
    return (
      <div>
        <h1>Home</h1>
      </div>
    );
  }
}

export default Home;

//
import React from 'react';

class Detail extends React.Component {
  render() {
    return (
      <div>
        <h1>Detail</h1>
      </div>
    );
  }
}

export default Detail;

//
import React from 'react';

class Write extends React.Component {
  render() {
    return (
      <div>
        <h1>write</h1>
      </div>
    );
  }
}

export default Write;

 

-index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { BrowserRouter } from 'react-router-dom';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,

);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

 

>> 화면 연결까지 완료


(2) 부트 스트랩

(2-1) 설치

>>yarn add react-bootstrap bootstrap

 

(2-2) public  / index.html - <head> 태그 안에 입력

    <!--부트스트랩 사용을 위한 Link -->
    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
      integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
      crossorigin="anonymous"
    />
    <script
      src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
      integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
      crossorigin="anonymous"
    ></script>

 

(3) 화면 그리기

(3-1) Home.js

import React from 'react';

class Home extends React.Component {
  render() {
    return (
      //html 부분은 아래에 따로 첨부
    );
  }
}

export default Home;
<div>
{/* header */}
<header>
  <div class="p-5 mb-4 bg-light rounded-3 text-center">
    <div class="container-fluid py-5">
      <h1 class="display-5 fw-bold">React와 SrpingBoot를 활용한 게시판</h1>
      <p>목록 보기</p>
    </div>
  </div>
</header>

{/* main */}
<main class="container">
  <section>
    <article class="row">
      <div class="col">
        <table class="table table-hover table-striped text-center">
          <colgroup>
            <col width="10%" />
            <col width="40%" />
            <col width="15%" />
            <col width="25%" />
            <col width="10%" />
          </colgroup>
          <thead>
            <tr>
              <th>글번호</th>
              <th>제목</th>
              <th>글쓴이</th>
              <th>등록시간</th>
              <th>조회수</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>1</td>
              <td>테스트 제목</td>
              <td>테스트 내용</td>
              <td>2022.04.27 12:50:00</td>
              <td>0</td>
            </tr>
            <tr>
              <td>2</td>
              <td>테스트 제목</td>
              <td>테스트 내용</td>
              <td>2022.04.27 12:50:00</td>
              <td>0</td>
            </tr>
          </tbody>
          <tfoot></tfoot>
        </table>
      </div>
      <div class="d-flex justify-content-end">
        <button class="btn btn-primary">글쓰기</button>
      </div>
    </article>
  </section>
</main>

{/* footer */}
<footer class="container-fluid py-5 mt-5 border-top">
  <p class="lead text-mute text-center">made by bitc</p>
</footer>
</div>

>>웹 브라우저

(3-2) Detail.js

import React from 'react';

class Detail extends React.Component {
  render() {
    return (
      <div>
        {/* header */}
        <header>
          <div class="p-5 mb-4 bg-light rounded-3 text-center">
            <div class="container-fluid py-5">
              <h1 class="display-5 fw-bold">React와 SrpingBoot를 활용한 게시판</h1>
              <p>목록 보기</p>
            </div>
          </div>
        </header>

        {/* main */}
        <main class="container">
          <section>
            <article class="">
              <div class="row">
                <div class="col-sm-12">
                  <div class="my-3">
                    <input type="text" class="form-control" id="title" />
                  </div>
                </div>
              </div>
              <div class="row mb-3">
                <div class="col-sm-4">
                  <input type="text" class="form-control" id="create-id" />
                </div>
                <div class="col-sm-4">
                  <input type="text" class="form-control" id="hit-cnt" />
                </div>
                <div class="col-sm-4">
                  <input type="text" class="form-control" id="create-date" />
                </div>
              </div>

              <div class="row mb-3">
                <div class="col-sm-12">
                  <textarea rows="5" class="form-control" id="content"></textarea>
                </div>
              </div>
              <div class="my-3 d-flex justify-content-between">
                <div>
                  <button class="btn btn-secondary">목록</button>
                </div>
                <div>
                  <button class="btn btn-primary mx-3">등록</button>
                  <button class="btn btn-danger">삭제</button>
                </div>
              </div>
            </article>
          </section>
        </main>

        {/* footer */}
        <footer class="container-fluid py-5 mt-5 border-top">
          <p class="lead text-mute text-center">made by bitc</p>
        </footer>
      </div>
    );
  }
}

export default Detail;

(3-3) Write.js

import React from 'react';

class Write extends React.Component {
  render() {
    return (
      <div>
        {/* header */}
        <header>
          <div class="p-5 mb-4 bg-light rounded-3 text-center">
            <div class="container-fluid py-5">
              <h1 class="display-5 fw-bold">React와 SrpingBoot를 활용한 게시판</h1>
              <p>목록 보기</p>
            </div>
          </div>
        </header>

        {/* main */}
        <main class="container">
          <section>
            <article>
              <div class="row">
                <div class="col-sm-6 mx-auto">
                  <div class="my-3">
                    <label for="title">Title : </label>
                    <input
                      type="text"
                      class="form-control"
                      id="title"
                      name="title"
                      placeholder="Enter title"
                    />
                  </div>
                  <div class="my-3">
                    <label for="user-id">ID : </label>
                    <input
                      type="text"
                      class="form-control"
                      id="user-id"
                      name="user-id"
                      placeholder="Enter user ID"
                    />
                  </div>
                  <div class="my-3">
                    <label for="content">Content : </label>
                    <textarea
                      rows="10"
                      class="form-control"
                      id="content"
                      name="content"
                      placeholder="Enter content"
                    ></textarea>
                  </div>
                  <div class="my-3 d-flex justify-content-between">
                    <div>
                      <button class="btn btn-secondary">목록</button>
                    </div>
                    <div>
                      <button class="btn btn-primary mx-3">등록</button>
                      <button class="btn btn-warning">취소</button>
                    </div>
                  </div>
                </div>
              </div>
            </article>
          </section>
        </main>

        {/* footer */}
        <footer class="container-fluid py-5 mt-5 border-top">
          <p class="lead text-mute text-center">made by bitc</p>
        </footer>
      </div>
    );
  }
}

export default Write;

 

(+) App.js

// import logo from './logo.svg';
import './App.css';
// import AxiosTest from './axiosTest';
import React from 'react';
import { HashRouter, Route } from 'react-router-dom';

import Home from './routes/Home';
import Detail from './routes/Detail';
import Write from './routes/Write';
import Navigation from './components/Navigation';

function App() {
  return (
    <HashRouter>
      <Navigation />
      <Route path="/" exact={true} component={Home} />
      <Route path="/detail/:boardno" exact={true} component={Detail} /> //boardno 추가
      <Route path="/write" exact={true} component={Write} />
    </HashRouter>
    // <div className="App">
    //   <AxiosTest/>
    // </div>
  );
}

export default App;

 

>> 화면 구상 완료<<

 


(4) 데이터 가져오기

-글 번호 가져와서 axios 사용해 전송하는 방식

(4-1) Write.js

import React from 'react';
import axios from 'axios';

class Write extends React.Component {
  state = {
    title: '',
    content: '',
    userId: '',
  };

  //Change - input 태그의 onChange랑 한쌍
  titleChange = (e) => {
    this.setState({ title: e.target.value });
  };
  userIdChange = (e) => {
    this.setState({ userId: e.target.value });
  };
  contentChange = (e) => {
    this.setState({ content: e.target.value });
  };

  //데이터 보내기
  sendData = () => {
    const datas = {
      title: this.state.title,
      content: this.state.content,
      createId: this.state.userId,
    };
    console.log(datas);
    axios
      .post('/ajax/boardWrite', null, { params: datas })
      .then((res) => {
        console.log('통신성공');
        console.log(res.data);
      })
      .catch((err) => {
        console.log('통신실패');
        console.log(err);
      });
  };
  render() {
    return (
      <div>
        {/* header */}
        ...
        {/* main */}
        <main class="container">
          ...
                  <div class="my-3 d-flex justify-content-between">
                    <div>
                      <button class="btn btn-secondary">목록</button>
                    </div>
                    <div>
                      <button class="btn btn-primary mx-3" onClick={this.sendData}>
                        등록
                      </button>
                      <button class="btn btn-warning">취소</button>
                    </div>
                  </div>
                </div>
              </div>
            </article>
          </section>
        </main>

        {/* footer */}
...
    );
  }
}

export default Write;

>> sts에서 ReactBoardController.java 글쓰기 부분 삭제

//글 쓰기
	@RequestMapping(value="/ajax/boardWrite", method=RequestMethod.POST)
	public Object ajaxBoardInsert(BoardDto board) throws Exception {
		System.out.println(board);
//		return "success";
		boardService.insertBoard(board);
		return "success";
		//원래는 if문을 써서 해당값이 들어갔는지 확인하는 작업이 필요한데..간단하게 하는 거라 생략
	}

 

>> 웹브라우저에서 데이터 입력하고 글쓰기버튼 눌렀을 때, 디비에 데이터 들어가는지 확인

 

(4-2) Detail.js

import React from 'react';
import axios from 'axios';
import { Link } from 'react-router-dom';

class Detail extends React.Component {
  state = {
    boardNo: 1,
    title: '',
    content: '',
    userId: '',
    createDate: '',
    hitCnt: 0,
  };

  componentDidMount() {
    const boardNo = this.props.match.params.boardno; //글 번호 가져와서 axios 사용해 전송하는 방식 사용할 예정
    const url = `/ajax/boardDetail/${boardNo}`;
    axios
      .get(url)
      .then((res) => {
        console.log('통신성공');
        console.log(res);
        this.setState({
          boardNo: res.data.boardNo,
          title: res.data.title,
          content: res.data.content,
          userId: res.data.createId,
          createDate: res.data.createDate,
          hitCnt: res.data.hitCnt,
        });
      })
      .catch((err) => {
        console.log('통신실패');
        console.log(err);
      });
  }

  render() {
    const data = this.props.match.params.boardno;
    console.log(data);
    return (
      <div>
        {/* header */}
        <header>
          <div class="p-5 mb-4 bg-light rounded-3 text-center">
            <div class="container-fluid py-5">
              <h1 class="display-5 fw-bold">React와 SrpingBoot를 활용한 게시판</h1>
              <p>목록 보기</p>
            </div>
          </div>
        </header>

        {/* main */}
        <main class="container">
          <section>
            <article class="">
              <div class="row">
                <div class="col-sm-12">
                  <div class="my-3">
                    <input type="text" class="form-control" id="title" value={this.state.title} />
                  </div>
                </div>
              </div>
              <div class="row mb-3">
                <div class="col-sm-4">
                  <input
                    type="text"
                    class="form-control"
                    id="create-id"
                    value={this.state.userId}
                  />
                </div>
                <div class="col-sm-4">
                  <input type="text" class="form-control" id="hit-cnt" value={this.state.hitCnt} />
                </div>
                <div class="col-sm-4">
                  <input
                    type="text"
                    class="form-control"
                    id="create-date"
                    value={this.state.createDate}
                  />
                </div>
              </div>

              <div class="row mb-3">
                <div class="col-sm-12">
                  <textarea
                    rows="5"
                    class="form-control"
                    id="content"
                    value={this.state.content}
                  ></textarea>
                </div>
              </div>
              <div class="my-3 d-flex justify-content-between">
                <div>
                  <button class="btn btn-secondary">
                    <Link to="/">목록</Link>
                  </button>
                </div>
                <div>
                  <button class="btn btn-primary mx-3">등록</button>
                  <button class="btn btn-danger">삭제</button>
                </div>
              </div>
            </article>
          </section>
        </main>

        {/* footer */}
...
      </div>
    );
  }
}

export default Detail;

>>웹 브라우저 : db에서 delete_yn이 N인 글의 번호를 입력해야 함.

http://localhost:3000/#/detail/5

 

 

(4-3) Home.js

import React from 'react';

class Home extends React.Component {
  render() {
    return (
      <div>
        {/* header */}
        <header>
          <div class="p-5 mb-4 bg-light rounded-3 text-center">
            <div class="container-fluid py-5">
              <h1 class="display-5 fw-bold">React와 SrpingBoot를 활용한 게시판</h1>
              <p>목록 보기</p>
            </div>
          </div>
        </header>

        {/* main */}
        <main class="container">
          <section>
            <article class="row">
              <div class="col">
                <table class="table table-hover table-striped text-center">
                  <colgroup>
                    <col width="10%" />
                    <col width="40%" />
                    <col width="15%" />
                    <col width="25%" />
                    <col width="10%" />
                  </colgroup>
                  <thead>
                    <tr>
                      <th>글번호</th>
                      <th>제목</th>
                      <th>글쓴이</th>
                      <th>등록시간</th>
                      <th>조회수</th>
                    </tr>
                  </thead>
                  <tbody>
                    <tr>
                      <td>1</td>
                      <td>테스트 제목</td>
                      <td>테스트 내용</td>
                      <td>2022.04.27 12:50:00</td>
                      <td>0</td>
                    </tr>
                    <tr>
                      <td>2</td>
                      <td>테스트 제목</td>
                      <td>테스트 내용</td>
                      <td>2022.04.27 12:50:00</td>
                      <td>0</td>
                    </tr>
                  </tbody>
                  <tfoot></tfoot>
                </table>
              </div>
              <div class="d-flex justify-content-end">
                <button class="btn btn-primary">글쓰기</button>
              </div>
            </article>
          </section>
        </main>

        {/* footer */}
        <footer class="container-fluid py-5 mt-5 border-top">
          <p class="lead text-mute text-center">made by bitc</p>
        </footer>
      </div>
    );
  }
}

export default Home;
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/06   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함