본문 바로가기
Spring

[Spring] MVC2 (2) ajax - 1, 2번째 버튼

by moca7 2024. 10. 18.

 

 

 

ㅁ ajax는 포워드도 redirect도 아니다.

 

 

 

ㅁ src/main/webapp/WEB-INF/views에 member 폴더 생성

- member 폴더에 manage1.jsp, manage2.jsp 생성

 

 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
   
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
    <h3>회원관리 1번 페이지</h3>

</body>
</html>
 

 

 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
   
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
    <h3>회원관리 2번 페이지</h3>

</body>
</html>
 

 

 

 

 

 

ㅁ main.jsp

 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
   
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

    <h3>메인페이지 입니다</h3>

    <a href="${contextPath}/member/manage1.do">회원관리1</a> <br>
    <a href="${contextPath}/member/manage2.do">회원관리2</a> <br>

</body>
</html>
 

 

 

- url mapping값이다. controller 클래스가 필요하다.

- ".do"를 빼면 ~와 비슷하다.

 

 

 

 

ㅁ MvcController

 

 
package com.br.ajax.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class MvcController {
   
   
    private Logger logger = LoggerFactory.getLogger(MvcController.class);
   

    @RequestMapping(value= {"/", "/main.do"})
    public String mainPage() {
       
        // 출력문은 성능저하를 야기시킴 => 로그 출력 권장
        // System.out.println("MvcController의 mainPage 메소드 작동됨");
       
        /*
        // 다음과 같은 레벨로 다음과 같은 메세지가 출력되도록 작성. 각각의 레벨별로 출력.
        logger.trace("trace msg");
        logger.debug("debug msg");
        logger.info("info msg");
        logger.warn("warn msg");
        logger.error("error msg");
        // slf4j는 fatal은 없다.
        */

        return "main";
    }


    @GetMapping("/member/manage1.do")
    public void memberManagePage1() {}
   

    @GetMapping("/member/manage2.do")
    public void memberManagePage2() {}
   

}

 

- 메인 페이지 요청할때마다 매번 출력되니까 로그는 주석처리한다. 

- @GetMapping, @PostMapping 습관 들이기

- 리턴한다면 member/manage1, member/manage2를 리턴한다. 맞나?

 

 

 

 

 

 

ㅁ manage1.jsp

 

 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
   
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
    <h3>회원관리 1번 페이지</h3>

    <form id="mem-form">
        번호 : <input type="text" name="userNo"> <br>
        아이디 : <input type="text" name="userId"> <br>
        비밀번호 : <input type="text" name="userPwd"> <br>
       
        <button type="button">조회1(아이디, 비번으로 이름 조회)</button>
        <button type="button">조회2(아이디, 비번으로 이름 조회)</button>
        <button type="button">조회3(번호로 회원전체정보 조회)</button>
        <button type="button">전체조회</button>
        <br>
       
        <button type="button" onclick="">번외1</button>
        <button type="button" onclick="">번외2</button>
    </form>
   
    <hr>
   
    <div id="result">
        결과가 보여지는 영역
    </div>
   
</body>
</html>
 

 

- form 안에 버튼을 두지만 submit시키지 않고 함수를 실행시킬 예정이다.

 

 

 

 

ㅁ com.br.ajax.dto 패키지에 MemberDto 클래스 생성

 

 
package com.br.ajax.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
public class MemberDto {

    private int userNo;
    private String userId;
    private String userPwd;
    private String userName;
   
}

 

- db를 쓰진 않고 조회됐다는 가정하에 ~

- 롬복 사용. 필드 가지고 저것들이 만들어짐.

 

 

 

 

 

 

ㅁ com.br.ajax.service 패키지에 MemberService 인터페이스 생성

 

 
package com.br.ajax.service;

public interface MemberService {

    // 아이디와 비번 가지고 특정 회원 이름 조회하는 서비스
    String selectMemberByIdPwd(String userId, String userPwd);

    // 번호 가지고 회원전체정보 조회하는 서비스
    MemberDto selectMemberByNo(int userNo);

    // 회원 전체 목록을 조회하는 서비스
    List<MemberDto> selectMemberList();
   
}

 

 

 

 

ㅁ MemberServiceImpl 구현클래스 생성

 

 
package com.br.ajax.service;

import java.util.Arrays;
import java.util.List;

import org.springframework.stereotype.Service;

import com.br.ajax.dto.MemberDto;

@Service
public class MemberServiceImpl implements MemberService {

    // 이게 db에 저장된 정보라고 가정
    private List<MemberDto> list = Arrays.asList(
                                        new MemberDto(1, "user01", "pass01", "홍길동")
                                      , new MemberDto(2, "user02", "pass02", "김말똥")
                                      , new MemberDto(3, "user03", "pass03", "강개순")
                                                );


    @Override
    public String selectMemberByIdPwd(String userId, String userPwd) {
        for(MemberDto m : list) {
            if(m.getUserId().equals(userId) && m.getUserPwd().equals(userPwd)) {
                return m.getUserName();
            }
        }
        return null;
    }
   

    @Override
    public MemberDto selectMemberByNo(int userNo) {
        for(MemberDto m : list) {
            if(m.getUserNo() == userNo) {
                return m;
            }
        }
        return null;
    }
   

    @Override
    public List<MemberDto> selectMemberList() {
        return list;
    }

}

 

- @Service 어노테이션으로 빈 등록.

 

 

 

 

 

 

ㅁ com.br.ajax.controller 패키지에 MemberController1 클래스 생성

 

 
package com.br.ajax.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import com.br.ajax.service.MemberService;

import lombok.RequiredArgsConstructor;

@RequestMapping("/member1")
@RequiredArgsConstructor
@Controller
public class MemberController1 {

    private final MemberService memService;
   
    private Logger logger = LoggerFactory.getLogger(MemberController1.class);
   
   
    @GetMapping("/detail1.do")
    public void memberDetail(String id, String pwd) {
        logger.debug("request id: {}, pwd: {}", id, pwd);
    }
   
   
}

 

- @Controller 어노테이션으로 빈 등록.

- 롬복의 @RequiredArgsConstructor을 써서 final이 붙은 필드의 매개변수 생성자만 만든다. 

 

- @RequestMapping("/member1")으로 ~

 

- memberDetail메소드의 매개변수에 넘어오는 값의 키값과 동일하게 작성하면 @RequestParam을 안써도 됨.

 

- 로거에 printf문처럼 패턴을 작성할 수 있다.

%d, %f, %s 이런거 필요 없이 어떤 타입이든 가능하다. 문자든 실수든 객체든. 

 

 

 

 

 

ㅁ manage1.jsp 수정

 

 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
   
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>

<script src="${contextPath}/resources/js/jquery-3.7.1.min.js"></script>

</head>
<body>
    <h3>회원관리 1번 페이지</h3>

    <form id="mem-form">
        번호 : <input type="text" name="userNo"> <br>
        아이디 : <input type="text" name="userId"> <br>
        비밀번호 : <input type="text" name="userPwd"> <br>
       
        <button type="button" onclick="fn_ajax1()">조회1(아이디, 비번으로 이름 조회)</button>
        <button type="button" onclick="">조회2(아이디, 비번으로 이름 조회)</button>
        <button type="button" onclick="">조회3(번호로 회원전체정보 조회)</button>
        <button type="button" onclick="">전체조회</button>
        <br>
       
        <button type="button" onclick="">번외1</button>
        <button type="button" onclick="">번외2</button>
    </form>
   
    <hr>
   
    <div id="result">
        결과가 보여지는 영역
    </div>

    <script>
        // 조회1 버튼 : 아이디랑 비번으로 이름을 조회
        function fn_ajax1() {

            let id = $("input[name=userId]").val();
            let pwd = $("input[name=userPwd]").val();

            $.ajax({
                // 요청
                url: "${contextPath}/member1/detail1.do",
                data: "id=" + id + "&pwd=" + pwd, // 쿼리스트링으로 전달값 작성
                type: "get",

                // 응답
                success: function(){

                },
                error: function(){

                }
            })

        }
    </script>
   
</body>
</html>
 

 

 

- jQuery라이브러리가 없다.  2번째 프로젝트의 src/main/webapp/assets/js의 jquery-3.7.1.min.js를 가져온다.

- ajax data 속성을 쿼리스트링, 객체 방식으로 할 수 있었다.

- type에 get이나 post를 쓸 수 있었다. 생략시 get이었다. 

 

- success에는 통신에 성공했을 때 실행할 함수를, error에는 통신에 실패했을 때 실행할 함수를, complete에는 성공 여부와 상관없이 항상 실행할 함수를 작성한다.

- AJAX 요청 시 서버에서 돌아오는 응답 데이터는 success 콜백 함수의 첫 번째 인자로 전달되며, 이 함수 내에서 응답 데이터를 처리하거나 화면에 반영할 수 있습니다. 이를 통해 서버와 클라이언트 간의 비동기 통신이 가능합니다.

 

 

 

 

 

 

 

 

- 아이디와 비밀번호를 입력하고 '조회1(아이디, 비번으로 이름 조회)' 클릭시 콘솔에 로그가 찍힌다. 

C:\logs의 ajax.log와 ajax-2024-10-18.0.log에도 저게 찍혀있다. 

(ConsoleAppender, FileAppender, RollingFileAppender에 로그가 기록된다.)

 

 

 

 

 

 

 

ㅁ MemberController1 수정

 

 
package com.br.ajax.controller;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import com.br.ajax.service.MemberService;

import lombok.RequiredArgsConstructor;

@RequestMapping("/member1")
@RequiredArgsConstructor
@Controller
public class MemberController1 {

    private final MemberService memService;
   
    private Logger logger = LoggerFactory.getLogger(MemberController1.class);
   

   
    /* 기존의 HttpServletResponse 객체 이용하는 방식

    @GetMapping("/detail1.do")
    public void memberDetail(String id, String pwd, HttpServletResponse response) throws IOException {
        logger.debug("request id: {}, pwd: {}", id, pwd);
        String result = memService.selectMemberByIdPwd(id, pwd);
       
        response.setContentType("text/html; charset=utf-8");
        PrintWriter out = response.getWriter();
        out.print(result);
    }
    */
   
   
    // Spring 방식
    @GetMapping("/detail1.do")
    public String memberDetail(String id, String pwd) {
 
        logger.debug("request id: {}, pwd: {}", id, pwd);
 
        String result = memService.selectMemberByIdPwd(id, pwd);
       
        return result;
    }
   
}
 

 

 

- ajax 요청은 forward도, redirect도 하면 안된다.

- 로그를 찍을 때 그냥 { }를 두는것 만으로 출력 형식을 설정할 수 있다.

 

 

 

(1) 기존 방식

 

 

 

(2) Spring 방식

 

- 기본적으로 return result;로 문자열 리턴하면 포워딩하려 한다. 그래서 404 에러가 발생할 수 있다.

- return 하는 문자열이 응답 뷰가 아니라 응답 데이터라는 것을 알려줘야 한다.

- 메소드 상단에 @ResponseBody 어노테이션을 붙이면 지금 반환하는 값이 응답 뷰가 아니라 응답 데이터로 할 수 있다.

안 붙이면 기본적으로 응답 뷰로 인식한다.

 

- 돌려보내는 데이터에 대한 타입을 써줘야 한다. 안 쓰면 한글 데이터는 깨질 수 있다. 단순한 숫자나 영문이면 안해도 상관없지만. 한글 데이터는 무조건 써줘야 한다.

- @GetMapping, @PostMapping의 produces 속성에 응답 데이터에 대한 MIME 타입을 쓸 수 있다.

 

 

 

 

※ @ResponseBody

- 비동기식으로 데이터 응답시 필요한 어노테이션

- 해당 어노테이션이 붙은 메소드에서의 반환 값은 응답 뷰가 아닌 어떤 data(text, json, xml 등)라는 걸 의미한다.

 

 

 

 

 

 

 

 

- 개발자 도구의 Network탭에서 각 요청을 클릭한다.

payload 탭에서 요청시 전달값을, response 탭에서 응답 데이터를 확인할 수 있다.

 

 

 

 

===========================================================================

 

 

ㅁ 2번째 버튼

 

 

ㅁ manage1.jsp 수정

 

 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
   
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>

<script src="${contextPath}/resources/js/jquery-3.7.1.min.js"></script>

</head>
<body>
    <h3>회원관리 1번 페이지</h3>

    <form id="mem-form">
        번호 : <input type="text" name="userNo"> <br>
        아이디 : <input type="text" name="userId"> <br>
        비밀번호 : <input type="text" name="userPwd"> <br>
       
        <button type="button" onclick="fn_ajax1();">조회1(아이디, 비번으로 이름 조회)</button>
        <button type="button" onclick="fn_ajax2();">조회2(아이디, 비번으로 이름 조회)</button>
        <button type="button" onclick="">조회3(번호로 회원전체정보 조회)</button>
        <button type="button" onclick="">전체조회</button>
        <br>
       
        <button type="button" onclick="">번외1</button>
        <button type="button" onclick="">번외2</button>
    </form>
   
    <hr>
   
    <div id="result">
        결과가 보여지는 영역
    </div>

    <script>
        // 조회1 버튼 : 아이디랑 비번으로 이름을 조회
        function fn_ajax1() {

            let id = $("input[name=userId]").val();
            let pwd = $("input[name=userPwd]").val();

            $.ajax({
                // 요청
                url: "${contextPath}/member1/detail1.do",
                data: "id=" + id + "&pwd=" + pwd, // 쿼리스트링으로 전달값 작성
                type: "get",

                // 응답
                success: function(resData){
                    $("#result").text(resData);
                },
                error: function(){

                }
            })

        }
   
   
        function fn_ajax2() {
           
            $.ajax({
                url: "${contextPath}/member1/detail2.do",
                type: "get",
                data: $("#mem-form").serialize(),

                success: function(resData){
                    $("#result").text(resData);
                },
                error: function(){
                    console.log("조회2 버튼에 대한 ajax 통신 실패");
                }
               
            })
           
        }
   
    </script>
   
</body>
</html>
 

 

 

- form의 모든 입력요소들을 직렬화 (위와 같은 쿼리스트링으로 만들어짐)

- 해당 form요소.serialize() => "userNo=10&userId=user01&userPwd=pass01"

- form 요소에 serialize() 메서드를 실행하면 해당 폼 데이터가 쿼리스트링 형태로 변환되며, 이때 쿼리스트링에 포함되는 각 key값은 폼 필드의 name 속성값이 된다.

 

 

 

 

 

ㅁ MemberController1 수정

 

 
package com.br.ajax.controller;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.br.ajax.service.MemberService;

import lombok.RequiredArgsConstructor;

@RequestMapping("/member1")
@RequiredArgsConstructor
@Controller
public class MemberController1 {

    private final MemberService memService;
   
    private Logger logger = LoggerFactory.getLogger(MemberController1.class);
   

   
    /* 기존의 HttpServletResponse 객체 이용하는 방식

    @GetMapping("/detail1.do")
    public void memberDetail(String id, String pwd, HttpServletResponse response) throws IOException {
        logger.debug("request id: {}, pwd: {}", id, pwd);
        String result = memService.selectMemberByIdPwd(id, pwd);
       
        response.setContentType("text/html; charset=utf-8");
        PrintWriter out = response.getWriter();
        out.print(result);
    }
    */
   
   
    // Spring 방식
    @ResponseBody
    @GetMapping(value="/detail1.do", produces="text/html; charset=utf-8")
    public String memberDetail(String id, String pwd) {
        logger.debug("request id: {}, pwd: {}", id, pwd);
        String result = memService.selectMemberByIdPwd(id, pwd);
       
        return result;
    }
   
   
    @ResponseBody
    @GetMapping(value="/detail2.do", produces="text/html; charset=utf-8")
    public String memberDetail2(String userId, String userPwd) {
 
        return memService.selectMemberByIdPwd(userId, userPwd); // String 변수 안두고 1줄로 줄이기. 
 
    }
   
   
}

 

 

 

 

 

- form요소를 직렬화하는 걸 보여드리려 했다. 직접 쿼리스트링으로 쪼개도 되고 직렬화해도 되고.

 

 

ㅁ 지금 응답 데이터로 문자열을 응답하고 있는데 dto, List를 응답할 예정이다.

- 스프링에서는 응답데이터를 리턴하고 @ResponseBody하고 반환형만 바꿔준다.

json, gson을 안써도 된다.

- 대신 자바에서의 객체를 응답하고자 한다면 그게 내부적으로 json 데이터로 변환되서 전달해야함.

자동으로 변환해주는 라이브러리가 있다. 담주에 배움.