포스코DX X 비트교육센터 6기 - Front-Back API


예제: Emaillist

Backend API 만들기

  • pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  
  <!-- 부모 스타터 -->
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.7.16</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	
  <groupId>com.poscodx</groupId>
  <artifactId>emaillist</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>
  
  <properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.build.outputEncoding>UTF-8</project.build.outputEncoding>
		<java.version>17</java.version>
	</properties>
	
	<dependencies>
		<!-- Spring Boot Dev Tools: Live Reload -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
		</dependency>

		<!-- Spring Boot Web Starter -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		
		<!-- MyBatis Starter -->
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>2.2.0</version>
		</dependency>
		
		<!-- lombok -->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>

		<!-- mariadb jdbc driver -->		
		<dependency>
		    <groupId>org.mariadb.jdbc</groupId>
		    <artifactId>mariadb-java-client</artifactId>
		</dependency>

		<!-- Spring Boot Test Integration -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>
	
	<build>
		<finalName>emaillist</finalName>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
				<exclude>
						<groupId>org.projectlombok</groupId>
						<artifactId>lombok</artifactId>
					</exclude>
						<exclude>
							<groupId>org.springframework.boot</groupId>
							<artifactId>spring-boot-devtools</artifactId>		
						</exclude>
					</excludes>
				</configuration>
			</plugin>			
		</plugins>		
	</build>	
</project>
  • application.yml
# spring configuration
spring:
   application:
      name: emaillist
   
   # devtools - live reload
   devtools:
      livereload:
         enabled: true
            
   #mvc
   mvc:
      static-path-pattern: /assets/**
      throw-exception-if-no-handler-found: true
            
   #datasource
   datasource:
      driver-class-name: org.mariadb.jdbc.Driver
      url: jdbc:mariadb://[ip]/webdb?charset=utf8
      username: [username]
      password: [password]
      hikari:
         minimum-idle: 10
         maximum-pool-size: 20

# mybatis
mybatis:
   config-location: classpath:mybatis/configuration.xml
   
# server
server:
   port: 8080
   servlet:
      context-path: /
      encoding:
         charset: utf-8
         enabled: true
         
# logging
logging:
   pattern:
      console: "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{5} - %msg%n"
   level:
      root: INFO
  • EmailistApplication.java : 스프링부트 실행 class
package com.poscodx.emaillist;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class EmaillistApplication {
	public static void main(String[] args) {
		SpringApplication.run(EmaillistApplication.class, args);
	}
}

  • configuration.xml : MyBatis 프레임워크의 설정 파일
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<settings>
		<setting name="mapUnderscoreToCamelCase" value="true"/>
	</settings>

	<typeAliases>
		<typeAlias type="com.poscodx.emaillist.vo.EmaillistVo" alias="emaillistvo" />
    </typeAliases>	

	<mappers>
		<mapper resource="mybatis/mappers/emaillist.xml" />
	</mappers>
</configuration>
  • emaillist.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="emaillist">

	<select id="findAll" resultType="emaillistvo">
		<![CDATA[
		  select no, first_name, last_name, email
		    from emaillist 
        order by no desc
		]]>
	</select>

	<insert id="insert" parameterType="emaillistvo">
		<![CDATA[
		insert
		  into emaillist
		values (null, #{firstName }, #{lastName }, #{email })
		]]>
	</insert>
</mapper>
  • JsonResult.java : JSON 형식의 응답을 생성하는 데 사용
package com.poscodx.emaillist.dto;

public class JsonResult {
	private String result;	//"success" or "fail"
	private Object data;	// if success, set! 
	private String message; // if fail, set!
	
	private JsonResult(Object data) {
		this.result = "success";
		this.data = data;
	}

	private JsonResult(String message) {
		this.result = "fail";
		this.message = message;
	}
	
	public String getResult() {
		return result;
	}
	public Object getData() {
		return data;
	}
	public String getMessage() {
		return message;
	}

	public static JsonResult success(Object data) {
		return new JsonResult(data);
	}
	
	public static JsonResult fail(String message) {
		return new JsonResult(message);
	}
}
  • GlobalExceptionHandler.java : 전역 예외 처리 클래스로 클라이언트에게 이해하기 쉬운 오류 메시지를 제공한다.
package com.poscodx.emaillist.exception;

import java.io.PrintWriter;
import java.io.StringWriter;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.NoHandlerFoundException;

import com.poscodx.emaillist.dto.JsonResult;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
	@ResponseBody
	@ExceptionHandler(Exception.class)
	public ResponseEntity<JsonResult> handlerException(Exception e) {
		
		// 로깅(Logging)
		StringWriter errors = new StringWriter();
		e.printStackTrace(new PrintWriter(errors));
		log.error(errors.toString());

		// 응답
		JsonResult jsonResult =
				(e instanceof NoHandlerFoundException) ?
						JsonResult.fail("Unknown Request") :
						JsonResult.fail(errors.toString());
		
		return ResponseEntity
				.status(HttpStatus.OK)
				.body(jsonResult);
	}
}
  • EmaillistVo.java : 룸북을 적용한 경우이다.
package com.poscodx.emaillist.vo;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Setter
@Getter
@ToString
public class EmaillistVo {
	private Long no;
	private String firstName;
	private String lastName;
	private String email;

}
  • APIControllor.java
package com.poscodx.emaillist.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import com.poscodx.emaillist.dto.JsonResult;
import com.poscodx.emaillist.repository.EmaillistRepository;

@RestController
public class ApiController {
	@Autowired
	private EmaillistRepository emaillistRepository;
	
	@GetMapping("/api")
	public ResponseEntity<JsonResult> read() {
		return ResponseEntity
				.status(HttpStatus.OK)
				.body(JsonResult.success(emaillistRepository.findAll()));
	}
}
  • EmaillistRepository.java
package com.poscodx.emaillist.repository;

import java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.poscodx.emaillist.vo.EmaillistVo;

@Repository
public class EmaillistRepository {
	
	@Autowired
	private SqlSession sqlSession;

	public List<EmaillistVo> findAll() {
		return sqlSession.selectList("emaillist.findAll");
	}
}

Frontend

  • App.js
import React, { useState, useEffect } from "react";
import "./assets/scss/App.scss";
import RegisterForm from "./RegisterForm";
import SearchBar from "./SearchBar";
import Emaillist from "./Emaillist";
// import data from './assets/json/data';

function App() {

// emaillist 저장 상태 변수
// 초기값 null 설정
  const [emails, setEmails] = useState(null);

// email을 keyword를 통해 찾아내는 함수
  const searchEmail = (keyword) => {
    const newEmails = data.filter(
      (email) =>
        email.firstName.indexOf(keyword) !== -1 ||
        email.lastName.indexOf(keyword) !== -1 ||
        email.email.indexOf(keyword) !== -1
    );
    setEmails(newEmails);
  };

// post를 통해 서버로 전송하는 함수.
// fetch를 사용하여 서버의 /api 엔드포인트로 POST 요청
  const addEmail = async (email) => {
    fetch("/api", {
      method: "post",
      headers: {
        "Content-Type": "application/json",
        Accept: "application/json",
      },
      body: JSON.stringify(email),
    });
  };

// 서버로부터 이메일 목록을 가져오는 함수
// fetch를 사용하여 서버의 /api 엔드포인트로 GET 요청
  const fetchList = async () => {
    try {
      const response = await fetch("/api", {
        method: "get",
        headers: {
          "Content-Type": "application/json",
          Accept: "application/json",
        },
        body: null,
      });

      if (!response.ok) {
        throw new Error(`${response.status} ${response.statusText}`);
      }

      const json = await response.json();

      if (json.result !== "success") {
        throw new Error(`${json.result} ${json.message}`);
      }

      console.log(json.data);

// 요청이 성공하면 emails 상태 변수를 업데이트
      setEmails(json.data);
    } catch (err) {
      console.error(err);
    }
  };

// useEffect 훅을 사용하여 애플리케이션이 처음 렌더링될 때 fetchList 함수를 호출
// 빈 배열의 효과로 한 번만 실행
  useEffect(() => {
    fetchList();
  }, []);

  return (
    <div id={"App"}>
      <RegisterForm addEmail={addEmail} />
      <SearchBar searchEmail={searchEmail} />
      {emails === null ? null : <Emaillist emails={emails} />}
    </div>
  );
}

export { App };

image