6 minute read

lombok

import lombok.Data;

@Data
public class StudentVO {
	private int idx;
	private String name;
	private String email;
	private int grade;		
}

롬복(Lombok)라이브러리를 통해 자원관리 메서드 등에 대한 자동 생성을 할 수 있다. VO 클래스 정의 시 어노테이션을 통해 자동 생성할 대상을 지정하면 된다. Outline뷰에서 생성된 것들을 확인할 수 있다.

  1. @Getter
    Getter 메서드 자동 생성
  2. @Setter
    Setter 메서드 자동 생성
  3. @ToString
    ToString 메서드 자동 생성
  4. xxxConstructor
    생성자 자동 생성(기본 생성자, 파라미터 생성자 등)
  5. @Data
    모든 기본 사항에 대한 자동 생성

IOC/DI

JSP 에서 객체 생성 과정

  1. A객체 생성
  2. A객체가 B객체를 필요로 할 때 A클래스 내부에서 B클래스의 객체(인스턴스)를 생성한다.
    • ‘A객체가 B객체에 의존적이다’
    • A클래스 내부에서 의존성 객체(B)를 생성한다.
  3. A클래스 내부에서 의존성 객체(B)의 메서드를 호출한다.

스프링에서 객체 생성 과정

  1. A객체 생성
  2. 의존성 객체 주입(DI)
    • A객체가 B객체를 직접 생성하는 것이 아니라 스프링에 제어권을 위임하여 스프링이 생성한 객체를 자동으로 주입받는다.
  3. 의존성 객체의 메서드 호출

IOC(Inversion Of Control) - 제어의 역전

지금까지는 개발자가 프로그램의 제어권을 갖는 주체(필요할 때 직접 객체 생성 관리)였지만 스프링에서는 프로그램의 제어권을 개발자가 아닌 스프링을 관리하는 컨테이너가 담당한다.

DI(Dependency Injection) - 의존성 주입

스프링에서 필요한 객체를 직접 생성하는 것이 아니라 외부에서 자동으로 생성된 객체를 주입받는 것이다.

Controller

컨트롤러 클래스가 서비스 클래스에 의존적일 때 서비스 클래스에 대한 인스턴스를 직접 생성하지 않고, 의존성 자동 주입(DI)을 통해 접근dl 가능하다.

  1. 생성자를 통해 의존성 객체를 주입받는 방법

     @Controller
     public class MyBatisController {
         private StudentService service;
    
         public MyBatisController(StudentService service) {
             super();
             this.service = service;
         }
     }
    

    컨트롤러 클래스의 생성자를 통해 자동 주입받을 객체의 타입 파라미터를 명시하면 컨트롤러 클래스의 인스턴스 생성 시점에 StudentService 클래스의 인스턴스를 주입받을 수 있다. 단, 해당 클래스(StudentService)에 @Service 등의 어노테이션 지정이 필수다.

  2. Setter 메서드를 통해 의존성 객체를 주입받는 방법(메서드에 @Autowired 어노테이션 필수)

     @Controller
     public class MyBatisController {
     	private StudentService service;
    
         @Autowired
         public void setService(StudentService service) {
             System.out.println("Test Setter");
             this.service = service;
         }
     }
    

    자동 주입받을 객체 타입 멤버변수 선언 후 Setter메서드 정의 시 Setter 메서드에 @Autowired 어노테이션을 적용하여 자동으로 메서드가 호출되도록 하면 해당 객체에 접근하는 시점에 자동으로 Setter 메서드를 통해 객체를 주입받을 수 있다.

  3. 의존성 주입받을 멤버변수 선언 시 @Autowired 어노테이션을 지정하는 방법

     @Controller
     public class MyBatisController {
     	@Autowired
         private StudentService service;
     }
    

MyBatis

Controller

@Controller
public class MyBatisController {
    @Autowired
    private StudentService service;

    @PostMapping("registStudentPro")
	public String registStudentPro(StudentVO student, Model model) {
		int insertCount = service.registStudent(student);
		
		if(insertCount == 0) {
			model.addAttribute("msg", "학생정보 등록 실패!");
			return "fail_back";
		}
		
		return "redirect:/studentList";
	}

    @GetMapping("studentInfo")
	public String studentInfo(String email, Model model) {
        StudentVO student = service.getStudentInfo(email);
		model.addAttribute("student", student);
		
		return "student_info";
	}
	
	@GetMapping("studentList")
	public String studentList(Model model) {
		List<StudentVO> studentList = service.getStudentList();
		model.addAttribute("studentList", studentList);
		
		return "student_list";
	}
}

스프링에서 의존 관계에 있는 객체 생성 방법은 4가지 방법이 있다.

  1. 직접 인스턴스를 생성하는 방법
    StudentService service = new StudentService();이렇게 직접 생성하는 방법이다. 스프링에서는 의존성 객체 자동 주입(DI)기능으로 객체를 직접 생성하지 않아도 되므로 이 방법은 쓸 일이 없다.
  2. 생성자를 통해 의존성 객체 자동으로 주입받는 방법
  3. Setter 메서드를 통해 의존성 객체를 주입받는 방법
  4. 의존성 주입받을 멤버변수 선언 시 @Autowired 어노테이션을 지정
    2, 3, 4번의 방법 중 중 어느 방법을 사용하더라도 직접 인스턴스 생성 없이도 DI 에 의해 객체가 자동으로 주입되므로 service 멤버변수를 통해 바로 메서드 호출이 가능하다.

Service

@Service
public class StudentService {
	@Autowired
	private StudentMapper mapper;

	public int registStudent(StudentVO student) {
		return mapper.insertStudent(student); 
	}
}

MyBatis 를 통해 SQL 구문 처리를 담당할 XXXMapper.xml 파일과 연동되는 XXXMapper 객체를 자동 주입받기 위해 @Autowired 어노테이션으로 멤버변수를 선언한다. 단, @Mapper 어노테이션이 적용된 Mapper 인터페이스 정의가 필수다. (일반 클래스 아님!)

DB작업을 수행할 Mapper객체의 메서드를 호출하여 SQL구문 실행을 요청한다. DAO 클래스 없이 마이바티스 활용을 위한 Mapper객체의 메서드 호출 후 리턴되는 결과값을 전달받아 Controller클래스로 다시 리턴해주는 역할을 수행한다. 단, 별도의 추가적인 작업이 없으므로 return 문 뒤에 메서드 호출 코드를 직접 기술하고, 만약 메서드 호출 전후 추가적인 작업이 필요할 경우 호출 코드와 리턴문을 분리하면 된다.

Mapper 역할을 수행하는 XXXMapper 인터페이스는 인스턴스 생성이 불가능하며 스프링(마이바티스)에서 자동 주입으로 객체를 전달받아 사용한다. 업캐스팅을 통해 실제 구현체 객체를 알지 못하더라도 메서드 호출이 가능하다.

Mapper Interface

MyBatis 연동 시 DAO 클래스 대신 활용할 Mapper 인터페이스를 정의한다. 마이바티스를 통해 Mapper XML 파일과 연결되어 자동으로 XML 파일의 SQL 구문을 실행한다. 주의할 점은 인터페이스 내의 추상메서드의 이름은 XML 파일의 각 태그 ID 속성값과 동일하게 정의해야한다. @Mapper 어노테이션을 적용하여 Mapper 역할 수행하는 빈으로 등록(DI 활용)해야 한다.

@Mapper
public interface StudentMapper {
	int insertStudent(StudentVO student); // public abstract 생략되어 있음
}

Service 클래스로부터 호출받아 SQL 구문 실행을 위해 XML 파일과 연결될 추상메서드를 정의했다. 추상메서드명과 XML 파일의 태그 내의 id 속성값이 일치해야하며 이 때 root-context.xml 파일 내의 지정된 다음 코드에 의해 현재 인터페이스와 XML 파일 내의 namespace 속성에 지정된 객체가 자동으로 연결된다.

<property name="mapperLocations" value="classpath:/com/itwillbs/test3_mybatis/mapper/*Mapper.xml"></property>
<mybatis-spring:scan base-package="com.itwillbs.test3_mybatis"/>

작성한 insertStudent메서드는 자동으로 StudentMapper.xml 파일의 insert id="insertStudent" 태그와 연결되어 해당 태그 내의 SQL 구문을 자동으로 실행한다. 이 때, XML 에서 단일 파라미터일 경우 전달된 파라미터에 직접 접근이 가능하다.

Mapper XML

  • insert 태그 기본 문법

      <insert id="xxx">
          실행할 INSERT 구문...
      </insert>
    
      <?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.mytest.mytest.mapper.StudentMapper">
    
          <!-- 학생 정보 등록 - INSERT -->
          <!-- INSERT INTO xxx VALUES (null, ?, ?, ?) -->
          <!-- 자동으로 int 타입 리턴값 전달됨 -->
          <insert id="insertStudent">
              INSERT INTO student
              VALUES (
                  null  -- AUTO_INCREMENT 컬럼이므로 null 값 전달 시 자동 증가
                  , #{name}  -- student.name 대신 name(변수명)만 지정
                  , #{email}  -- student.email 대신 email(변수명)만 지정
                  , #{grade}  -- student.grade 대신 grade(변수명)만 지정
              )
          </insert>
      </mapper>
    

    자바의 JDBC PreparedStatement객체 사용 시 파라미터를 만능문자(?)로 처리했으나 마이바티스에서는 #{파라미터명} 으로 데이터를 표시(별도의 치환과정 불필요)한다. 이 때, 사용될 데이터(파라미터)는 반드시 Mapper 인터페이스에서 메서드 내의 파라미터로 지정되어야한다. Mapper 인터페이스에서 메서드 파라미터로 전달한 객체(XXXVO 등)가 존재할 경우 해당 객체 내의 멤버변수명을 파라미터명으로 직접 접근이 가능(멤버변수명 정확히 지정 필수)하다.
    단, 전달된 파라미터가 복수개일 경우 파라미터명을 구분해야한다. (#{xxx.멤버변수명} 필수)
    ex) StudentVO 객체의 name 변수 값을 파라미터로 지정 시 기존 DAO 클래스에서는 student.getName() 메서드를 호출하여 값을 전달받았지만 마이바티스에서는 #{name} 형식으로 접근이 가능하다.
    INSERT, UPDATE, DELETE 태그를 통해 SQL 구문 실행 후에는 자동으로 int 타입 결과값이 리턴된다. PreparedStatement 객체의 executeUpdate() 메서드와 동일하다.

  • select 태그 기본 문법

      <select id="xxx" resultType="조회된 결과가 저장되어 리턴될 타입">
          실행할 구문...
      </select>
    
      <?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.mytest.mytest.mapper.StudentMapper">
    
          <!-- 학생 상세정보 조회 - SELECT -->
          <!-- resultType 속성에 StudentVO 타입 명시할 때 패키지명까지 함께 포함 -->
          <!-- 파라미터 사용 시 email 변수 1개가 전달되었으므로 그대로 접근 가능함 -->
          <select id="selectStudent" resultType="com.mytest.mytest.vo.StudentVO">
              SELECT * FROM student WHERE email = #{email}
          </select>
          <!-- 조회 결과(회원 1명의 레코드)를 StudentVO 객체 생성 및 자동 저장 후 리턴 -->
            
          <!-- 학생 목록 조회 - SELECT -->
          <!-- 
          조회 결과가 단일 레코드일 경우 해당 레코드 저장이 가능한 VO 타입 또는 HashMap 타입을 resultType 속성에 지정
          만약, 조회 결과가 복수개의 레코드일 경우 단일 레코드 저장이 가능한 VO 타입 지정 시
          해당 객체에 1개 레코드를 저장하고, VO 객체들을 묶어서 다시 List 타입 객체에 자동으로 저장
          즉, 1개 게시물 정보가 StudentVO 객체에 저장되고, 전체가 List 객체에 저장되므로
          List 타입의 제네릭 타입으로 지정한 StudentVO 타입을 resultType 으로 지정
          -->
          <select id="selectStudentList" resultType="com.mytest.mytest.vo.StudentVO">
              SELECT * FROM student
          </select>
      </mapper>
    

    resultType 속성에 기술하는 결과 타입은 SELECT 구문을 실행했을 때 기대되는 조회 결과를 저장하여 리턴하기 위한 데이터타입을 명시한다. 이 때, 해당 변수명과 데이터베이스 테이블의 컬럼명이 일치하는 값만 저장된다. 만약, 해당 데이터를 객체에 저장 시 객체 생성도 자동으로 수행(객체가 복수개일 경우 List 객체 등도 자동으로 생성)한다. 단일 항목의 기본 데이터일 경우 기본 데이터타입(int 등)을 명시하면 되고, 복수개의 항목 데이터일 경우 해당 데이터가 묶음으로 관리될 객체 타입을 명시하면 된다.
    ex) StudentVO 타입, HashMap 타입 등
    단, 사용자 정의 클래스 타입 지정 시 패키지명까지 함께 지정(Alias 기능을 통해 경로명을 생략할 수도 있다.)해야 한다.

뷰 페이지

<h1>학생 상세정보 조회 페이지</h1>

번호 : ${student.idx }
이름 : ${student.name }
<h1>학생 목록 조회 페이지</h1>

<c:forEach var="student" items="${studentList }">
    <tr>
        <td>${student.idx }</td>
        <td>${student.name }</td>
        <td>${student.email }</td>
        <td>${student.grade }</td>
    </tr>
</c:forEach>

이렇게 데이터에 접근하여 출력할 수 있다.

Leave a comment