Vue

Ajax로 json 정보 출력하기_ cors 정책 위반 에러. Spring 연동

wintertreey 2024. 8. 7. 19:14

이클립스에서 json데이터확인

 

아파치톰캣 서버 띄워둔다

 

 

 

에러발생 Cors 정책 위반

Access to fetch at 'http://localhost:8080/wpro2/JikwonInfo.jsp' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled

 

 

해당 문제는 서버쪽에서 해결해야한다.

이클립스 파일에 해당 코드를 추가해주자.

 

해결완료!


문제

<요구사항>

db연동 : jpa

서버사이드 : 스프링

넘겨주는 데이터파일 : json

클라이언트사이드 : 뷰

 

dto, entity 구분하기

join(부서명)

restful하게

 

 

 

 

프로젝트 생성하기

 

 

application.properties

spring.application.name=sprweb22_rest_jpa

#mariadb server connect
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.datasource.url=jdbc:mariadb://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=123


#jpa hibernate setting
logging.level.org.hibernate.SQL=debug
logging.level.org.hibernate.type.descriptor.sql=trace
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDBDialect

 

에러발생

Description: Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured. Reason: Failed to determine a suitable driver class

 

해결방안

application.properties 파일을 작업해주자....

 

 

Entity작업

 

 

DTO작업

package pack.dto;

import jakarta.persistence.Column;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import pack.entity.Jikwon;

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class JikwonDto {
	
	private int jikwon_no;
	private String jikwon_name;
	private String buser_name;
	
	@Column(name="jikwon_jik")
	private String jik;
	private int jikwon_pay;
	
	public static JikwonDto toDto(Jikwon jikwon) { //entity to dto
		return JikwonDto.builder()
				.jikwon_no(jikwon.getJikwon_no())
				.jikwon_name(jikwon.getJikwon_name())
				.buser_name(jikwon.getBuser().getBuser_name())
				.jik(jikwon.getJik())
				.jikwon_pay(jikwon.getJikwon_pay())
				.build();
	}
}

 

 

 

JPA를 이용해서 규칙에 맞추어 메소드를 생성한다

package pack.repository;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;

import pack.entity.Jikwon;

public interface JikwonRepository extends JpaRepository<Jikwon, Integer>{
	
	//query method
	List<Jikwon> findByJik(String jik);
}

 

 

마지막작업 Controller

package pack.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import pack.dto.JikwonDto;
import pack.repository.JikwonRepository;

@CrossOrigin("*")
@RestController
//@RequestMapping("/jikwons")
public class JikwonController {
	@Autowired
	private JikwonRepository jikwonRepository;

	@GetMapping("/{jik}")
	public List<JikwonDto> getList(@PathVariable("jik") String jik) {
		// entity to dto
		List<JikwonDto> jlist = jikwonRepository.findByJik(jik).stream().map(JikwonDto::toDto).toList();

		return jlist;
	}
}

 

어노테이션 부분을 살펴보자.

@CrossOrigin을 통해 SOP정책위반 부분 문제를 해결하였다.

@GetMapping시 entity를 dto로 변경해주는작업을 통해 jpa 구조를 따라주었다.

 

 

 

json을 웹상에 띄워보았다.

 

 

vue에서 웹화면작업

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="https://unpkg.com/vue@3"></script>
  </head>
  <body>
    <div id="app">
      직급입력 :
      <input type="text" size="5" v-model="jik" />
      <button @click="onclick">확인</button>
      <table v-if="jikwon.length > 0">
        <!--조건부렌더링-->
        <tr>
          <th>사번</th>
          <th>이름</th>
          <th>부서명</th>
          <th>직급</th>
          <th>연봉</th>
        </tr>
        <tr v-for="(j, index) in jikwon" :key="index">
          <td>{{j.jikwon_no}}</td>
          <td>{{j.jikwon_name}}</td>
          <td>{{j.buser_name}}</td>
          <td>{{j.jik}}</td>
          <td>{{j.jikwon_pay}}</td>
        </tr>
        <tr>
          <td colspan="5">인원수 {{jikwon.length}}</td>
        </tr>
        <tr>
            <td colspan="5">연봉평균 {{avgPay}}</td>
        </tr>
      </table>
    </div>
    <script>
      const {createApp} = Vue;
      createApp({
          data(){
              return{
                  jikwon: [],
                  jik: "",
                  avgPay:0,
              };
          },
          methods: {
              onclick(){
                  if(this.jik === ""){
                      alert("직급을 입력해주세요");
                      return;
                  }
                  this.showFunc();
              },
              showFunc(){
                  fetch('http://localhost:8080/'+ this.jik)
                  .then(response => response.json()) // JSON으로 변환
                  .then(data => {
                      this.jikwon = data;

                      const totalPay = this.jikwon.reduce((sum, jd) => sum + parseInt(jd.jikwon_pay || 0, 10), 0);
                      this.avgPay = (totalPay / this.jikwon.length).toFixed(2);
                  })
                  .catch(error => {
                      console.log('오류: ', error);
                  });
              },
          },
      }).mount("#app");
    </script>
  </body>
</html>

 

 

tofixed(n)를 통해 소숫점n자리까지 자를 수 있었다.

 

reduce 메서드는 배열의 각 요소를 순회한다.

 

parseInt(jd.jikwon_pay || 0, 10)

jd.jikwon_pay가 정의되어 있으면 그 값을 정수로 변환하고, 정의되어 있지 않으면 0을 사용. parseInt의 두 번째 인자는 진법을 지정하는 매개변수. 10은 십진수를 의미.

 

출력결과

 

++

뷰의 html파일을 복사하여 이클립스의 스프링폴더에 넣어둔다. 

이때 static폴더에 넣어야함을 잊지말자. 

templates : thymeleaf 전용..!

 

 

 

Q. repository 파일 VS dao파일

A. repository

 

1. JPA Repository

  • 설명: Spring Data JPA는 JPA를 기반으로 하는 데이터 접근 계층을 효율적으로 관리하기 위해 JpaRepository 인터페이스를 제공합니다. 이 인터페이스를 사용하면 기본적인 CRUD(생성, 조회, 업데이트, 삭제) 작업을 자동으로 처리해줍니다.
  • 장점:
    • 자동 구현: Spring Data JPA는 JpaRepository를 상속받은 인터페이스에 대해 기본적인 CRUD 메소드를 자동으로 구현합니다.
    • 쿼리 메소드: 메소드 이름을 기반으로 쿼리를 자동으로 생성할 수 있습니다. 예를 들어, findByJikwonJik와 같은 메소드 이름으로 쿼리를 정의할 수 있습니다.
    • 페이징 및 정렬: 기본적인 페이징 및 정렬 기능을 지원합니다.
    • 유지보수 용이: Spring Data JPA의 표준화된 방식으로 코드가 간결해지고 유지보수가 쉬워집니다.

2. DAO (Data Access Object)

  • 설명: DAO는 데이터베이스 작업을 추상화하는 객체입니다. 직접 DAO 클래스를 작성할 경우, 데이터베이스 작업을 위한 SQL 쿼리 및 연결 관리 코드를 직접 작성해야 합니다.
  • 장점:
    • 세밀한 제어: 특정 쿼리 및 데이터베이스 작업을 세밀하게 제어할 수 있습니다.
    • 복잡한 쿼리: 복잡한 쿼리나 비즈니스 로직을 직접 구현할 수 있습니다.
  • 단점:
    • 작업량 증가: 쿼리 및 데이터베이스 작업을 직접 구현해야 하므로 개발 및 유지보수가 번거롭습니다.
    • 복잡성 증가: SQL 쿼리와 연결 관리를 직접 처리해야 하므로 코드의 복잡성이 증가합니다.

추천: JPA Repository 사용 이유

  1. 자동화된 CRUD 작업: JpaRepository는 기본적인 CRUD 작업을 자동으로 처리하므로, 반복적인 코드 작성을 피할 수 있습니다.
  2. 쿼리 메소드 지원: JPA의 메소드 쿼리 기능을 사용하면 복잡한 JPQL이나 SQL을 직접 작성하지 않고도 데이터 조회를 할 수 있습니다.
  3. Spring Data JPA 통합: Spring Boot와의 통합이 잘 되어 있어, Spring Boot의 다양한 기능과 함께 쉽게 사용할 수 있습니다.
  4. 유지보수성: 표준화된 방식으로 코드가 작성되어 유지보수가 용이합니다.

 

                 dto todto/toentity entity

controller              service                   db 

웬만하면 jpa인터페이스, 레퍼지토리 작업해주고, 중간에 todto, toentity작업을 service에서 해주자. 

 

 

 

Controller의 역할

컨트롤러는 JPA Repository를 사용하여 데이터베이스에서 데이터를 가져오고, 이를 DTO로 변환하여 클라이언트에 JSON 형태로 반환합니다.

 

 

Q. @GetMapping("/jikwons/{jik}")와 @RequestMapping("/jikwons") ?

  • @RequestMapping을 클래스 레벨에서 사용: 클래스의 기본 URL 경로를 설정하고, 메소드 레벨에서 상대적인 경로를 지정하는 경우 유용합니다. 이를 통해 여러 메소드에서 공통 경로를 쉽게 설정할 수 있습니다.
  • @GetMapping을 메소드 레벨에서 사용: 특정 HTTP GET 요청 경로를 설정하는 데 유용하며, 클래스 레벨에서 기본 경로를 설정하지 않는 경우에 사용합니다.

따라서, 클래스 레벨에서 @RequestMapping을 사용하고 메소드 레벨에서 @GetMapping을 사용하는 것이 일반적인 RESTful API 설계 원칙에 부합합니다. 이렇게 하면 코드의 가독성이 높아지고 유지보수가 쉬워집니다.