본문 바로가기
클라우드 데브옵스

Day74 2024.09.05.목 #코딩일기

by SSONG1521 2024. 9. 5.

 

folder src/views/members
vue MemberDetailView.vue
<template>
  <div>
    <h1>MemberDetailView</h1>
    <h2>{{ member.name }}</h2>
    <p>{{ member.sal }}</p>
    <hr class="my-4" />
    <!-- <p>params: {{ $route.params }}</p>
    <p>Query: {{ $route.query }}</p>
    <p>hash: {{ router.hash }}</p> -->

    <div class="row g-2">
      <div class="col-auto">
        <button class="btn btn-outline-dark">이전글</button>
      </div>
      <div class="col-auto">
        <button class="btn btn-outline-dark">다음글</button>
      </div>
      <div class="col-auto me-auto"></div>
      <div class="col-auto">
        <button class="btn btn-outline-dark" @click="goListPage">목록</button>
      </div>
      <div class="col-auto">
        <button class="btn btn-outline-primary" @click="goMemberUpdatePage">수정</button>
      </div>
      <div class="col-auto">
        <button class="btn btn-outline-danger" @click="remove">삭제</button>
        api --> deleteMember(props.id)
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { deleteMember, getMemberById } from '@/api/members'
import router from '@/router'

// import { useRoute } from 'vue-router'

const props = defineProps({
  id: String
})
// const route = useRouter()

const member = ref({
  name: null,
  sal: null
})

const fetchMembers = async () => {
  console.log('1. memberPostValue props.id -> ', props.id)
  //오류발생지점
  const { data } = await getMemberById(props.id)
  console.log('3.PostDetailValue data -> ', data)
  SetMember(data)
}

const SetMember = ({ name, sal }) => {
  member.value.name = name
  member.value.sal = sal
}
fetchMembers()

const remove = async () => {
  try {
    if (confirm('당신은 진정 이 멤버를 삭제하시겠습니까?')) {
      await deleteMember(props.id)
      router.push({ name: 'MemberList' })
    }
  } catch (error) {
    console.error(error)
  }
}

const goListPage = () => router.push({ name: 'MemberList' })

const goMemberUpdatePage = () => {
  console.log('1.goMemberUpdatePage props.id-> ', props.id)

  router.push({
    name: 'MemberEdit',
    params: { id: props.id }
  })
}
</script>

<style lang="scss" scoped></style>
view 단

 

 

 

 



folder src/router
js index.js
import AboutView from '@/views/AboutView.vue'
import HomeView from '@/views/HomeView.vue'
import MemberCreateView from '@/views/members/MemberCreateView.vue'
import MemberDetailView from '@/views/members/MemberDetailView.vue'
import MemberEditView from '@/views/members/MemberEditView.vue'
import MemberListView from '@/views/members/MemberListView.vue'
import NestedHomeView from '@/views/nested/NestedHomeView.vue'
import NestedOneView from '@/views/nested/NestedOneView.vue'
import NestedTwoView from '@/views/nested/NestedTwoView.vue'
import NestedView from '@/views/nested/NestedView.vue'
import { createRouter, createWebHistory } from 'vue-router'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: HomeView
  },
  {
    path: '/about',
    name: 'About',
    component: AboutView
  },
  {
    path: '/members',
    name: 'MemberList',
    component: MemberListView
  },
  {
    path: '/members/create',
    name: 'MemberCreate',
    component: MemberCreateView
  },
  {
    path: '/members/detail/:id',
    name: 'MemberDetail',
    component: MemberDetailView,
    props: true
  },
  {
    path: '/members/:id/edit',
    name: 'MemberEdit',
    component: MemberEditView,
    props: true
  },
  {
    path: '/nested',
    name: 'Nested',
    component: NestedView,
    children: [
      {
        path: '',
        name: 'NestedHome',
        component: NestedHomeView
      },
      {
        path: 'one',
        name: 'NestedOne',
        component: NestedOneView
      },
      {
        path: 'two',
        name: 'NestedTwo',
        component: NestedTwoView
      }
    ]
  }
]

const router = createRouter({
  history: createWebHistory('/'),
  routes: routes
})

export default router
view 단
 

 

 

 



folder src/views/members
vue MemberEditView.vue
<template>
  <div>
    <h2>회원(Member) 수정</h2>
    <hr class="my-4" />
    <MemberForm v-model:sal="form.sal" v-model:name="form.name" @submit.prevent="memberUpdate">
      <template #actions>
        <button type="button" class="btn btn-outline-danger" @click="goDetailPage">취소</button>
        <button class="btn btn-primary">수정</button>
      </template>
    </MemberForm>
  </div>
</template>

<script setup>
import { getMemberById, updateMember } from '@/api/members'
import MemberForm from '@/components/members/MemberForm.vue'
import { ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'

//route : parameter에 관련된 정보
const route = useRoute()
const router = useRouter()

const id = parseInt(route.params.id)
const form = ref({
  sal: null,
  name: null
})

const fetchMember = async () => {
  const { data } = await getMemberById(id)
  console.log('MemberEditValue id-> ', id)
  console.log('MemberEditValue data-> ', data)
  setForm(data)
}

const setForm = ({ sal, name }) => {
  form.value.sal = parseInt(sal)
  form.value.name = name
}
fetchMember()

const memberUpdate = async () => {
  try {
    console.log('MembersEditValue memberUpdate form.value-> ', form.value)

    const { data } = await updateMember(id, { ...form.value })
    console.log('await ... updateMember data-> ', data)

    router.push({ name: 'MemberDetail', params: { id } })
  } catch (error) {
    console.error(error)
  }
}

const goDetailPage = () => router.push({ name: 'MemberDetail', params: { id } })
</script>

<style lang="scss" scoped></style>

 

view 단
 

 

** emit 은 하위 component가 상위 component에 보내주면 된다.

 

 

 

 

 

 

folder src/api
js member.js
import axios from 'axios'

export function getMembers() {
  console.log('getMembers Start...')
  //axios는 ajax 프로그램과 같은
  return axios.get('http://localhost:8389/restApi/v1/members') //Risk API
}

//신규 members 등록
export function createMember(data) {
  console.log('신규 members 등록(createMember)..')
  console.log('신규 members 등록 data ->' + data)
  return axios.post('http://localhost:8389/restApi/v2/memberSave', data)
}

//단일 member 조회
export function getMemberById(id) {
  console.log('getMemberById typeof id-> ', typeof id)
  console.log('getMemberById id-> ', id)
  return axios.get('http://localhost:8389/restApi/v15/members/' + id)
}

export function updateMember(id, data) {
  console.log('updateMember typeof id-> ', typeof id)
  console.log('updateMember id-> ', id)

  //수정이기 때문에 put이다.
  return axios.put('http://localhost:8389/restApi/v21/members/' + id, data)
}

export function deleteMember(id) {
  console.log('deletePost id-> ', id)
  return axios.delete('http://localhost:8389/restApi/v21/deleteMember/' + id)
}
view 단
 

 

 

 

 

 

삭제 로직을 추가하기 위해서 spring에서 delete 추가해주기

 

Spring Starter Project oBootJpaApi01
package com.oracle.oBootMybatis01.controller
class JpaRestApiController
package com.oracle.oBootJpaApi01.controller;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collector;
import java.util.stream.Collectors;

import javax.naming.spi.DirStateFactory.Result;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.oracle.oBootJpaApi01.domain.Member;
import com.oracle.oBootJpaApi01.service.MemberService;

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

//@Controller + @ResponseBody = @RestController
@RestController
//private static final Logger logger = LoggerFactory.getLogger(JpaRestApiController.class);
@Slf4j
@RequiredArgsConstructor
public class JpaRestApiController {
	private final MemberService memberService;
	
	@RequestMapping("/helloText")
	public String helloText() {
		System.out.println("JpaRestApiController Start...");
		String hello = "안녕";
		// 		StringConverter
		return hello;
	}
	
	//Bad Api
	@GetMapping("/restApi/v1/members")
	public List<Member> membersVer1() {
		System.out.println("JpaRestApiController /restApi//v1/members start...");
		//return return되어서 결국에는 listMember에 쌓일 것이다.
		List<Member> listMember = memberService.getListAllMember();
		System.out.println("JpaRestApiController /restApi//v1/members listMember.size() -> "+listMember.size());
		return listMember;
	}
	
	//Good Api Easy Version
	//목표 : 이름 & 급여 만 전송
	@GetMapping("/restApi/v21/members")
	public Result membersVer21() {
		List<Member> fineMembers = memberService.getListAllMember();
		System.out.println("JpaRestApiController restApi/v21/members findMembers.size()-> "+fineMembers.size());
		
		//보내주는 Data의 List를 새로 만들었다. (넘어온 list 값이 과도하기 때문에)
		List<MemberRtnDto> resultList = new ArrayList<MemberRtnDto>();
		
		//이전 목적 : 반드시 필요한 Data만 보여준다. (외부 노출 최대한 금지)
		//향상형 for문을 이용
		for(Member member: fineMembers) {
			//아래 문이 생성자가 없이도 잘 실행되는 이유 -> MemberRtnDto에 @AllArgsConstructor를 걸어주었기 때문에
			MemberRtnDto memberRtnDto = new MemberRtnDto(member.getName(), member.getSal());
			System.out.println("/restApi/v21/members getName -> "+memberRtnDto.getName());
			System.out.println("/restApi/v21/members getSal -> "+memberRtnDto.getSal());
			resultList.add(memberRtnDto);
		}
		System.out.println("/restApi/v21/members resultList.size()"+resultList.size());
		return new Result(resultList.size(), resultList);
	}
	
	//Good API 람다버전
	//목표 : 이름 & 급여 만 전송
	@GetMapping("/restApi/v22/members")
	public Result membersVer22() {
		List<Member> fineMembers = memberService.getListAllMember();
		System.out.println("JpaRestApiController restApi/v22/members findMembers.size()-> "
																	+fineMembers.size());
		// 자바 8에서 추가한 스트림(Streams)은 람다(->)를 활용할 수 있는 기술 중 하나
		//람다 형식 : 화살표 (->) 이용하는 것, stream()을 써 주어야지만 가능
		List<MemberRtnDto> memberCollect = 
				fineMembers.stream()
				//				m이라해도 되고 member라고 해도 된다.
				//				m이 있는 만큼 향상형 for문처럼 돌아가는 것
				//			하지만 map은 list로 수집을 하지는 못한다. 갖고만 있게 하는 로직
						   .map(m->new MemberRtnDto(m.getName(), m.getSal()))
						   //따라서 list로 만들어주는 작업을 해야한다.
						   .collect(Collectors.toList())
						   ;
		
		System.out.println("/restApi/v22/members memberCollect.size()-> "+memberCollect.size());
		//Result 생성자 (@AllArgsConstructor로 인해 생성자 정의 안해도 만들 수 있다.)
		//					 totCount				T data
		return new Result(memberCollect.size(), memberCollect);
	}
	
	@Data
	@AllArgsConstructor
	class Result<T> {
		private final int totCount;	//총 인원수 추가
		//들어오는 Data가 어떤 데이터형이든지 상관없이 T에 잡힌다. (Api의 경우는 T를 쓰는게 좋다.)
		private final T data;
	}
	
	//innerclass를 쓴 이유는 controller에서 값을 보내줄 때 딱 한번 쓰기 때문에
	//만일 이 로직을 service와 dao에도 넘겨준다면 domain(DTO)에 class를 만들어주면 된다.
	@Data
	//Data = getter + setter + Tostring
	//@AllArgsConstructor는 생성자
	@AllArgsConstructor
	class MemberRtnDto {
		//name과 sal만 뽑아서 data 전송해준다.
		//생성자가 없는 이유 -> AllArgsConstructor때문에!!
		//만약 하나를 final로 만들게 되면, @RequiredArgsConstructor로!!
		private String name;
		private Long   sal;
	}
	
	//Ver1. Bad
	@PostMapping("/restApi/v1/memberSave")
	// @RequestBody : Json(member)으로 온것을  --> Member member Setting
	public CreateMemberResponse saveMemberV1(@RequestBody @Valid Member member) {
		System.out.println("JpaRestApiController /api/v1/memberSave member-> "+member);
		System.out.println("JpaRestApiController /api/v1/memberSave member.getId()-> "+member.getId());
		log.info("member.getName()-> {}.",member.getName());
		log.info("member.getSal()-> {}.", member.getSal());
		
		Long id = memberService.saveMember(member);
		//그냥 보내면 String으로 보내지지만(id), 생성자로 보내기 위해서 새로 만들어서 보내야 Json 으로 보내진다.
		return new CreateMemberResponse(id);
	}
	
	//Ver2.
	// 목적  : Entity Member member --> 직접 화면이나 API위한 Setting 금지
	// 예시  : @NotEmpty  -->	@Column(name = "userName")
	@PostMapping("/restApi/v2/memberSave")
	// @RequestBody : Json(member)으로 온것을  --> Member member Setting 
	public CreateMemberResponse saveMemberV2(@RequestBody @Valid CreateMemberRequest cMember) {

		System.out.println("JpaRestApiController /api/v2/memberSave cMember->"+cMember);
		log.info("member.getName()-> {}.", cMember.getName());
		log.info("member.getSal()-> {}.", cMember.getSal());
		Member member = new Member();
		member.setName(cMember.getName());
		member.setSal(cMember.getSal());
		
		Long id = memberService.saveMember(member);
		return new CreateMemberResponse(id);
	}

	
	/*
	 *   단일 Id 조회 API
	 *   URI 상에서 '{ }' 로 감싸여있는 부분과 동일한 변수명을 사용하는 방법
	 *   해당 데이터가 있으면 업데이트를 하기에 
	 *  GET요청이 여러번 실행되어도 해당 데이터는 같은 상태이기에 멱등
	 */
	//BadApi (이유: 보안문제, 전부 다 보내줘서!!)
	@GetMapping("/restApi/v15/members/{id}")
	public Member membersVer15(@PathVariable("id") Long id) {
		System.out.println("JpaRestApiController restApi/v15/members id-> "+id);
		Member findMember = memberService.findByMember(id);
		System.out.println("JpaRestApiController restApi/v15/members findMember->"+findMember);
		
		return findMember;
	}

	@Data
	static class CreateMemberRequest {
		@NotEmpty
		private String name;
		private Long sal;
	}
	
	@Data
	@RequiredArgsConstructor
	class CreateMemberResponse {
		private final Long id;
		//위에 @RequiredArgsConstructor를 썼기 때문에 생략해도 된다.
//		public CreateMemberResponse(Long id) {
//			this.id = id;
//		}
	}
	
	 @PutMapping("/restApi/v21/members/{id}")
	 public UpdateMemberResponse updateMemberV21(@PathVariable("id") Long id,
			                                     @RequestBody @Valid UpdateMemberRequest uMember) {
		 System.out.println("JpaRestApiController updateMemberV21 id->"+id);
		 System.out.println("JpaRestApiController updateMemberV21 uMember->"+uMember);
		 memberService.updateMember(id, uMember.getName(), uMember.getSal());
		 Member findMember = memberService.findByMember(id);
		 return new UpdateMemberResponse(findMember.getId(),findMember.getName(), findMember.getSal() );
	 }

	
//	@PutMapping("/restApi/v21/members/{id}")
//	public UpdateMemberResponse updateMemberV21(@PathVariable("id") Long id,
//												@RequestBody @Valid UpdateMemberRequest uMember) {
//		System.out.println("JpaRestApiController updateMemberV21 start...");
//		System.out.println("JpaRestApiController updateMemberV21 id ->"+id);
//		System.out.println("JpaRestApiController updateMemberV21 uMember ->"+uMember);
//		memberService.updateMember(id, uMember.getName(), uMember.getSal());
//		Member findMember = memberService.findByMember(id);
//		return new UpdateMemberResponse(findMember.getId(), findMember.getName(), findMember.getSal());
//	}
	
	 //수정
	 @DeleteMapping("/restApi/v21/deleteMember/{id}")
	 													//{id}를 id로 놓고 쓰겠다.
	 public CreateMemberResponse deleteMemberV21(@PathVariable("id") Long id) {
		 System.out.println("JpaRestApiController /restApi/v21/deleteMember/{id} start...");
		 System.out.println("JpaRestApiController updateMemberV21 id-> "+id);
		 memberService.deleteMember(id);
		 //JSON 형태로 
		 return new CreateMemberResponse(id);
	 }
	 

	@Data
	static class UpdateMemberRequest {
		private String name;
		private Long sal;
	}
	
	@Data
	@AllArgsConstructor
	class UpdateMemberResponse {
		private Long id;
		private String name;
		private Long sal;
	}
}
Console
 

 

 - delete를 해주기 위해 spring에 추가해준다.

 

 

 



Spring Starter Project oBootJpaApi01
package com.oracle.oBootMybatis01.service
class MemberService
package com.oracle.oBootJpaApi01.controller;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collector;
import java.util.stream.Collectors;

import javax.naming.spi.DirStateFactory.Result;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.oracle.oBootJpaApi01.domain.Member;
import com.oracle.oBootJpaApi01.service.MemberService;

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

//@Controller + @ResponseBody = @RestController
@RestController
//private static final Logger logger = LoggerFactory.getLogger(JpaRestApiController.class);
@Slf4j
@RequiredArgsConstructor
public class JpaRestApiController {
	private final MemberService memberService;
	
	@RequestMapping("/helloText")
	public String helloText() {
		System.out.println("JpaRestApiController Start...");
		String hello = "안녕";
		// 		StringConverter
		return hello;
	}
	
	//Bad Api
	@GetMapping("/restApi/v1/members")
	public List<Member> membersVer1() {
		System.out.println("JpaRestApiController /restApi//v1/members start...");
		//return return되어서 결국에는 listMember에 쌓일 것이다.
		List<Member> listMember = memberService.getListAllMember();
		System.out.println("JpaRestApiController /restApi//v1/members listMember.size() -> "+listMember.size());
		return listMember;
	}
	
	//Good Api Easy Version
	//목표 : 이름 & 급여 만 전송
	@GetMapping("/restApi/v21/members")
	public Result membersVer21() {
		List<Member> fineMembers = memberService.getListAllMember();
		System.out.println("JpaRestApiController restApi/v21/members findMembers.size()-> "+fineMembers.size());
		
		//보내주는 Data의 List를 새로 만들었다. (넘어온 list 값이 과도하기 때문에)
		List<MemberRtnDto> resultList = new ArrayList<MemberRtnDto>();
		
		//이전 목적 : 반드시 필요한 Data만 보여준다. (외부 노출 최대한 금지)
		//향상형 for문을 이용
		for(Member member: fineMembers) {
			//아래 문이 생성자가 없이도 잘 실행되는 이유 -> MemberRtnDto에 @AllArgsConstructor를 걸어주었기 때문에
			MemberRtnDto memberRtnDto = new MemberRtnDto(member.getName(), member.getSal());
			System.out.println("/restApi/v21/members getName -> "+memberRtnDto.getName());
			System.out.println("/restApi/v21/members getSal -> "+memberRtnDto.getSal());
			resultList.add(memberRtnDto);
		}
		System.out.println("/restApi/v21/members resultList.size()"+resultList.size());
		return new Result(resultList.size(), resultList);
	}
	
	//Good API 람다버전
	//목표 : 이름 & 급여 만 전송
	@GetMapping("/restApi/v22/members")
	public Result membersVer22() {
		List<Member> fineMembers = memberService.getListAllMember();
		System.out.println("JpaRestApiController restApi/v22/members findMembers.size()-> "
																	+fineMembers.size());
		// 자바 8에서 추가한 스트림(Streams)은 람다(->)를 활용할 수 있는 기술 중 하나
		//람다 형식 : 화살표 (->) 이용하는 것, stream()을 써 주어야지만 가능
		List<MemberRtnDto> memberCollect = 
				fineMembers.stream()
				//				m이라해도 되고 member라고 해도 된다.
				//				m이 있는 만큼 향상형 for문처럼 돌아가는 것
				//			하지만 map은 list로 수집을 하지는 못한다. 갖고만 있게 하는 로직
						   .map(m->new MemberRtnDto(m.getName(), m.getSal()))
						   //따라서 list로 만들어주는 작업을 해야한다.
						   .collect(Collectors.toList())
						   ;
		
		System.out.println("/restApi/v22/members memberCollect.size()-> "+memberCollect.size());
		//Result 생성자 (@AllArgsConstructor로 인해 생성자 정의 안해도 만들 수 있다.)
		//					 totCount				T data
		return new Result(memberCollect.size(), memberCollect);
	}
	
	@Data
	@AllArgsConstructor
	class Result<T> {
		private final int totCount;	//총 인원수 추가
		//들어오는 Data가 어떤 데이터형이든지 상관없이 T에 잡힌다. (Api의 경우는 T를 쓰는게 좋다.)
		private final T data;
	}
	
	//innerclass를 쓴 이유는 controller에서 값을 보내줄 때 딱 한번 쓰기 때문에
	//만일 이 로직을 service와 dao에도 넘겨준다면 domain(DTO)에 class를 만들어주면 된다.
	@Data
	//Data = getter + setter + Tostring
	//@AllArgsConstructor는 생성자
	@AllArgsConstructor
	class MemberRtnDto {
		//name과 sal만 뽑아서 data 전송해준다.
		//생성자가 없는 이유 -> AllArgsConstructor때문에!!
		//만약 하나를 final로 만들게 되면, @RequiredArgsConstructor로!!
		private String name;
		private Long   sal;
	}
	
	//Ver1. Bad
	@PostMapping("/restApi/v1/memberSave")
	// @RequestBody : Json(member)으로 온것을  --> Member member Setting
	public CreateMemberResponse saveMemberV1(@RequestBody @Valid Member member) {
		System.out.println("JpaRestApiController /api/v1/memberSave member-> "+member);
		System.out.println("JpaRestApiController /api/v1/memberSave member.getId()-> "+member.getId());
		log.info("member.getName()-> {}.",member.getName());
		log.info("member.getSal()-> {}.", member.getSal());
		
		Long id = memberService.saveMember(member);
		//그냥 보내면 String으로 보내지지만(id), 생성자로 보내기 위해서 새로 만들어서 보내야 Json 으로 보내진다.
		return new CreateMemberResponse(id);
	}
	
	//Ver2.
	// 목적  : Entity Member member --> 직접 화면이나 API위한 Setting 금지
	// 예시  : @NotEmpty  -->	@Column(name = "userName")
	@PostMapping("/restApi/v2/memberSave")
	// @RequestBody : Json(member)으로 온것을  --> Member member Setting 
	public CreateMemberResponse saveMemberV2(@RequestBody @Valid CreateMemberRequest cMember) {

		System.out.println("JpaRestApiController /api/v2/memberSave cMember->"+cMember);
		log.info("member.getName()-> {}.", cMember.getName());
		log.info("member.getSal()-> {}.", cMember.getSal());
		Member member = new Member();
		member.setName(cMember.getName());
		member.setSal(cMember.getSal());
		
		Long id = memberService.saveMember(member);
		return new CreateMemberResponse(id);
	}

	
	/*
	 *   단일 Id 조회 API
	 *   URI 상에서 '{ }' 로 감싸여있는 부분과 동일한 변수명을 사용하는 방법
	 *   해당 데이터가 있으면 업데이트를 하기에 
	 *  GET요청이 여러번 실행되어도 해당 데이터는 같은 상태이기에 멱등
	 */
	//BadApi (이유: 보안문제, 전부 다 보내줘서!!)
	@GetMapping("/restApi/v15/members/{id}")
	public Member membersVer15(@PathVariable("id") Long id) {
		System.out.println("JpaRestApiController restApi/v15/members id-> "+id);
		Member findMember = memberService.findByMember(id);
		System.out.println("JpaRestApiController restApi/v15/members findMember->"+findMember);
		
		return findMember;
	}

	@Data
	static class CreateMemberRequest {
		@NotEmpty
		private String name;
		private Long sal;
	}
	
	@Data
	@RequiredArgsConstructor
	class CreateMemberResponse {
		private final Long id;
		//위에 @RequiredArgsConstructor를 썼기 때문에 생략해도 된다.
//		public CreateMemberResponse(Long id) {
//			this.id = id;
//		}
	}
	
	 @PutMapping("/restApi/v21/members/{id}")
	 public UpdateMemberResponse updateMemberV21(@PathVariable("id") Long id,
			                                     @RequestBody @Valid UpdateMemberRequest uMember) {
		 System.out.println("JpaRestApiController updateMemberV21 id->"+id);
		 System.out.println("JpaRestApiController updateMemberV21 uMember->"+uMember);
		 memberService.updateMember(id, uMember.getName(), uMember.getSal());
		 Member findMember = memberService.findByMember(id);
		 return new UpdateMemberResponse(findMember.getId(),findMember.getName(), findMember.getSal() );
	 }

	
//	@PutMapping("/restApi/v21/members/{id}")
//	public UpdateMemberResponse updateMemberV21(@PathVariable("id") Long id,
//												@RequestBody @Valid UpdateMemberRequest uMember) {
//		System.out.println("JpaRestApiController updateMemberV21 start...");
//		System.out.println("JpaRestApiController updateMemberV21 id ->"+id);
//		System.out.println("JpaRestApiController updateMemberV21 uMember ->"+uMember);
//		memberService.updateMember(id, uMember.getName(), uMember.getSal());
//		Member findMember = memberService.findByMember(id);
//		return new UpdateMemberResponse(findMember.getId(), findMember.getName(), findMember.getSal());
//	}
	
	 //수정
	 @DeleteMapping("/restApi/v21/deleteMember/{id}")
	 													//{id}를 id로 놓고 쓰겠다.
	 public CreateMemberResponse deleteMemberV21(@PathVariable("id") Long id) {
		 System.out.println("JpaRestApiController /restApi/v21/deleteMember/{id} start...");
		 System.out.println("JpaRestApiController updateMemberV21 id-> "+id);
		 memberService.deleteMember(id);
		 //JSON 형태로 
		 return new CreateMemberResponse(id);
	 }
	 

	@Data
	static class UpdateMemberRequest {
		private String name;
		private Long sal;
	}
	
	@Data
	@AllArgsConstructor
	class UpdateMemberResponse {
		private Long id;
		private String name;
		private Long sal;
	}
}
Console
 

 

 - 

 - json으로 담아주기 때문에 CreateMemberResponse로 가진다.

 

 

 

 

 

 

 

 

 

Spring Starter Project oBootJpaApi01
package com.oracle.oBootMybatis01.repository
interface MemberRepository
package com.oracle.oBootJpaApi01.repository;

import java.util.List;

import com.oracle.oBootJpaApi01.domain.Member;

public interface MemberRepository {
	Long 			save(Member member);
	List<Member>	findAll();
	Member 			findByMember(Long memberId);
	int 			updateByMember(Member member);
	void 			deleteById(Long id);
}
Console
 

 

 

 

 

 

 

Spring Starter Project oBootJpaApi01
package com.oracle.oBootMybatis01.repository
class JpaMemberRepository

 

package com.oracle.oBootJpaApi01.repository;

import java.util.List;

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

import com.oracle.oBootJpaApi01.domain.Member;

import jakarta.persistence.EntityManager;
import lombok.RequiredArgsConstructor;

@Repository
@RequiredArgsConstructor
public class JpaMemberRepository implements MemberRepository {
	//JPA는 반드시 Entity Manager를 써야한다. -> 이 이후에 injection이 되어야한다.
	//										생성자 or @RequiredArgsConstructor로!!
	private final EntityManager em;
	
	//final은 반드시 생성자에 들어가야한다. instance를 받아줄 곳이 없다.
	//따라서 final은 생성자를 반드시 넣어주어야 한다.
	// 생성자에 넣어주지 않으면, EntityManager를 구현할 수 가 없다.
	// 하지만 이 대신에 @RequiredArgsConstructor 가 이것과 같은 역할을 한다.
//	@Autowired
//	public JpaMemberRepository(EntityManager em) {
//		this.em = em;
//	}

	@Override
	public Long save(Member member) {
		System.out.println("JpaMemberRepository save before...");
		em.persist(member);
		return member.getId();
	}

	@Override
	public List<Member> findAll() {
		List<Member> memberList = em.createQuery("SELECT m FROM Member m", Member.class)
									.getResultList();
		System.out.println("JpaMemberRepository findAll memberList.size()-> "+memberList.size());
		return memberList;
	}

	@Override
	public Member findByMember(Long memberId) {
		Member member = em.find(Member.class, memberId);
		return member;
	}

	@Override
	public int updateByMember(Member member) {
		int result = 0;
		Member member3 = em.find(Member.class, member.getId());
		if (member3 != null) {
			// 회원 저장
			member3.setName(member.getName());
			member3.setSal(member.getSal());
			result = 1;
			System.out.println("JpaMemberRepository updateByMember Update...");
		} else {
			result = 0;
			System.out.println("JpaMemberRepository updateByMember No Exist..");
		}
		return result;
	}

	@Override
	public void deleteById(Long did) {
		System.out.println("JpaMemberRepository deleteById before...");
		Member member3 = em.find(Member.class, did);
		em.remove(member3);
		return;
		
	}

}
Console
 

 

 - 먼저 id를 찾아준 후 그 다음에 remove로 삭제해준다.

 

 

 

 



Spring Starter Project oBootJpaApi01
package com.oracle.oBootMybatis01
class WebConfig
package com.oracle.oBootJpaApi01;

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

//CORS는 한 도메인이 도메인 간의 요청을 가진 다른 도메인의 리소스에 액세스할 수 있게 해주는
//보안 메커니즘으로 최신 브라우저에서 구현된 동일 출처 정책 때문에 등장

@Configuration
public class WebConfig implements WebMvcConfigurer {
	@Override
	public void addCorsMappings(CorsRegistry registry) {
		// TODO Auto-generated method stub
//		WebMvcConfigurer.super.addCorsMappings(registry);
		registry.addMapping("/**")
				.allowedOrigins("*")
				.allowedMethods("GET", "POST", "PUT","DELETE")
				.maxAge(3000);
		
	}
}

 

 - DELETE를 추가시켜주어야 한다.

그 다음에 다시 도커에 스프링 해준다. (Day72 보고 따라하기)

 

 

그 다음 vue에서 delete관련해서 새로 수정시켜준 후 실행해보기