WebSquare + Spring 연동 예제 소스 코드 (MyBatis X)

이 예제는 "사용자 정보 조회" 및 "사용자 정보 등록" 기능을 구현합니다.

1. WebSquare 화면 (클라이언트)

userList.xml (화면 정의)

XML
 
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ev="http://www.w3.org/2001/xml-events"
    xmlns:w2="http://www.websquare.com/2001/WebSquare"
    xmlns:xq="http://www.w3.org/2005/xpath-functions">
    <head>
        <w2:type>DEFAULT</w2:type>
        <w2:buildDate/>
        <w2:exampleDataMap/>
        <w2:docType>HTML</w2:docType>
        <w2:uiInfo>
            <w2:w2ui chartid="" ev:onpageload="scwin.onpageload" gcmode="true" weak="false"/>
        </w2:uiInfo>
        <script type="javascript" lazy="false" src="/ui/user/userList.js"></script>
    </head>
    <body>
        <w2:dataMap baseNode="map" id="dataMap_search">
            <w2:udc_info>
                <w2:column dataType="text" name="userName"></w2:column>
            </w2:udc_info>
        </w2:dataMap>
        <w2:dataMap baseNode="map" id="dataMap_user">
            <w2:udc_info>
                <w2:column dataType="text" name="userId"></w2:column>
                <w2:column dataType="text" name="userName"></w2:column>
                <w2:column dataType="text" name="userEmail"></w2:column>
            </w2:udc_info>
        </w2:dataMap>
        <w2:dataList baseNode="list" id="dataList_userList" saveRemovedData="true">
            <w2:udc_info>
                <w2:column dataType="text" name="userId"></w2:column>
                <w2:column dataType="text" name="userName"></w2:column>
                <w2:column dataType="text" name="userEmail"></w2:column>
            </w2:udc_info>
        </w2:dataList>

        <w2:input id="input_searchUserName" style="width: 150px;" ref="dataMap_search.userName"></w2:input>
        <w2:button id="btn_search" style="width: 80px;height: 23px;" ev:onClick="scwin.btn_search_onClick" value="조회"></w2:button>
        <w2:button id="btn_new" style="width: 80px;height: 23px;" ev:onClick="scwin.btn_new_onClick" value="신규등록"></w2:button>

        <w2:grid id="grid_user" scrollByColumn="false" scrollByCell="false"
                 dataList="dataList_userList" style="width: 400px;height: 200px;">
            <w2:caption/>
            <w2:header style="">
                <w2:row style="">
                    <w2:column displayMode="label" id="column1" inputType="text" style="height:20px;" value="아이디" width="100"></w2:column>
                    <w2:column displayMode="label" id="column2" inputType="text" style="height:20px;" value="이름" width="150"></w2:column>
                    <w2:column displayMode="label" id="column3" inputType="text" style="height:20px;" value="이메일" width="150"></w2:column>
                </w2:row>
            </w2:header>
            <w2:gBody style="">
                <w2:row style="">
                    <w2:column displayMode="label" id="userId" inputType="text" width="100"></w2:column>
                    <w2:column displayMode="label" id="userName" inputType="text" width="150"></w2:column>
                    <w2:column displayMode="label" id="userEmail" inputType="text" width="150"></w2:column>
                </w2:row>
            </w2:gBody>
        </w2:grid>

        <w2:popup id="popup_userReg" style="width: 300px; height: 200px;">
            <w2:anchor winType="window" xpath="html"/>
            <w2:contents>
                <w2:input id="input_userId_reg" ref="dataMap_user.userId" style="width: 150px;"></w2:input>
                <w2:input id="input_userName_reg" ref="dataMap_user.userName" style="width: 150px;"></w2:input>
                <w2:input id="input_userEmail_reg" ref="dataMap_user.userEmail" style="width: 150px;"></w2:input>
                <w2:button id="btn_save" style="width: 80px;height: 23px;" ev:onClick="scwin.btn_save_onClick" value="저장"></w2:button>
            </w2:contents>
        </w2:popup>
    </body>
</html>

userList.js (JavaScript 로직)

JavaScript
 
scwin.onpageload = function() {
    // 페이지 로드 시 초기 데이터 조회 (선택 사항)
    // scwin.searchUserList();
};

// 사용자 조회 버튼 클릭 이벤트
scwin.btn_search_onClick = function() {
    scwin.searchUserList();
};

// 사용자 조회 함수 (READ)
scwin.searchUserList = function() {
    var searchParam = dataMap_search.getJSON(); // dataMap_search의 데이터를 JSON 형태로 가져옴

    WebSquare.request.execute( {
        id : "searchUser",
        action : "/api/users", // 조회 API URL
        method : "GET",
        mode : "asynchronous",
        mediaType : "application/json",
        requestData : searchParam, // 검색 조건 JSON
        successCallback : function(res) {
            // 서버 응답 (JSON)을 dataList_userList에 바인딩
            dataList_userList.setJSON(res.responseJSON.userList); // 서버 응답에서 'userList' 키의 값을 바인딩
            WebSquare.util.printStackTrace("사용자 목록 조회 성공: " + JSON.stringify(res.responseJSON));
        },
        errorCallback : function(res) {
            WebSquare.util.printStackTrace("사용자 목록 조회 실패: " + JSON.stringify(res.responseJSON));
            alert("사용자 목록 조회 실패했습니다.");
        }
    } );
};

// 신규등록 버튼 클릭 이벤트
scwin.btn_new_onClick = function() {
    dataMap_user.setEmptyAll(); // 등록용 dataMap 초기화
    $p.popup("popup_userReg").open(); // 등록 팝업 열기
};

// 사용자 저장 버튼 클릭 이벤트 (CREATE)
scwin.btn_save_onClick = function() {
    var userData = dataMap_user.getJSON(); // dataMap_user의 데이터를 JSON 형태로 가져옴

    if (!userData.userId || !userData.userName) {
        alert("아이디와 이름은 필수 입력 항목입니다.");
        return;
    }

    WebSquare.request.execute( {
        id : "saveUser",
        action : "/api/users", // 등록 API URL
        method : "POST",
        mode : "asynchronous",
        mediaType : "application/json",
        requestData : userData, // 사용자 정보 JSON
        successCallback : function(res) {
            alert("사용자 정보가 성공적으로 저장되었습니다.");
            $p.popup("popup_userReg").close(); // 팝업 닫기
            scwin.searchUserList(); // 목록 새로고침
        },
        errorCallback : function(res) {
            WebSquare.util.printStackTrace("사용자 저장 실패: " + JSON.stringify(res.responseJSON));
            alert("사용자 저장에 실패했습니다.");
        }
    } );
};

2. 백엔드 (Spring Boot 기준)

UserDto.java (DTO - Data Transfer Object)

Java
 
package com.example.websquareexample.dto;

public class UserDto {
    private String userId;
    private String userName;
    private String userEmail;

    // Getters and Setters (생성자를 사용해도 됩니다.)
    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getUserEmail() {
        return userEmail;
    }

    public void setUserEmail(String userEmail) {
        this.userEmail = userEmail;
    }

    @Override
    public String toString() {
        return "UserDto{" +
               "userId='" + userId + '\'' +
               ", userName='" + userName + '\'' +
               ", userEmail='" + userEmail + '\'' +
               '}';
    }
}

UserController.java (Controller)

Java
 
package com.example.websquareexample.controller;

import com.example.websquareexample.dto.UserDto;
import com.example.websquareexample.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController // @Controller + @ResponseBody (자동 JSON 변환)
@RequestMapping("/api")
public class UserController {

    @Autowired
    private UserService userService;

    // 사용자 목록 조회 (READ)
    @GetMapping("/users")
    public ResponseEntity<Map<String, Object>> getUsers(@RequestParam(required = false) String userName) {
        System.out.println("[Controller] getUsers 요청. 검색 조건: userName=" + userName);

        // 서비스 계층 호출
        List<UserDto> userList = userService.getUsers(userName);

        // WebSquare에 맞춰 Map 형태로 응답 구성 (userList 키로 List를 담음)
        Map<String, Object> response = new HashMap<>();
        response.put("userList", userList);
        response.put("message", "사용자 목록 조회 성공");

        return new ResponseEntity<>(response, HttpStatus.OK);
    }

    // 사용자 정보 등록 (CREATE)
    @PostMapping("/users")
    public ResponseEntity<Map<String, String>> createUser(@RequestBody UserDto userDto) {
        System.out.println("[Controller] createUser 요청. UserDto: " + userDto.toString());

        try {
            // 서비스 계층 호출
            userService.createUser(userDto);
            Map<String, String> response = new HashMap<>();
            response.put("message", "사용자 정보 등록 성공");
            return new ResponseEntity<>(response, HttpStatus.CREATED); // 201 Created
        } catch (Exception e) {
            System.err.println("[Controller] 사용자 등록 실패: " + e.getMessage());
            Map<String, String> errorResponse = new HashMap<>();
            errorResponse.put("message", "사용자 등록 실패: " + e.getMessage());
            return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST); // 400 Bad Request
        }
    }

    // UPDATE, DELETE는 유사한 방식으로 구현됩니다.
    // @PutMapping("/users/{userId}")
    // @DeleteMapping("/users/{userId}")
}

UserService.java (Service)

Java
 
package com.example.websquareexample.service;

import com.example.websquareexample.dao.UserDao;
import com.example.websquareexample.dto.UserDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
public class UserService {

    @Autowired
    private UserDao userDao; // DAO 의존성 주입

    // 사용자 목록 조회 비즈니스 로직 (READ)
    public List<UserDto> getUsers(String userName) {
        System.out.println("[Service] getUsers 호출. 검색 조건: " + userName);
        // 비즈니스 로직: 예를 들어, 검색 조건 가공, 권한 확인 등
        // 현재는 간단히 DAO에 그대로 전달
        return userDao.selectUsers(userName);
    }

    // 사용자 정보 등록 비즈니스 로직 (CREATE)
    @Transactional // 트랜잭션 관리 (여러 DB 작업이 묶일 때 사용)
    public void createUser(UserDto userDto) {
        System.out.println("[Service] createUser 호출. 등록할 사용자: " + userDto.toString());
        // 비즈니스 로직: 예를 들어, 아이디 중복 확인, 데이터 유효성 검사 등
        if (userDao.selectUserById(userDto.getUserId()) != null) {
            throw new RuntimeException("이미 존재하는 사용자 ID입니다: " + userDto.getUserId());
        }
        userDao.insertUser(userDto);
    }

    // UPDATE, DELETE에 대한 서비스 메서드도 유사하게 구현
}

UserDao.java (DAO - Data Access Object)

MyBatis를 사용하지 않으므로, 여기서는 간단하게 메모리 내(In-Memory) 데이터 저장소를 구현하여 DB 연동을 흉내 냅니다. 실제 DB 연동 시에는 JDBC 템플릿, JPA 등을 사용하여 구현합니다.

Java
 
package com.example.websquareexample.dao;

import com.example.websquareexample.dto.UserDto;
import org.springframework.stereotype.Repository;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Repository
public class UserDao {

    // 실제 DB 대신 사용할 In-Memory 저장소 (스레드 안전한 Map)
    private static final Map<String, UserDto> inMemoryDb = new ConcurrentHashMap<>();

    // 초기 데이터 추가 (테스트용)
    static {
        inMemoryDb.put("user01", createUserDto("user01", "김철수", "kim@example.com"));
        inMemoryDb.put("user02", createUserDto("user02", "이영희", "lee@example.com"));
        inMemoryDb.put("user03", createUserDto("user03", "박민수", "park@example.com"));
    }

    private static UserDto createUserDto(String userId, String userName, String userEmail) {
        UserDto userDto = new UserDto();
        userDto.setUserId(userId);
        userDto.setUserName(userName);
        userDto.setUserEmail(userEmail);
        return userDto;
    }

    // 사용자 목록 조회 (SELECT)
    public List<UserDto> selectUsers(String userName) {
        System.out.println("[DAO] selectUsers 호출. 검색 조건: " + userName);
        if (userName == null || userName.isEmpty()) {
            return new ArrayList<>(inMemoryDb.values()); // 모든 사용자 반환
        } else {
            // 이름으로 필터링
            return inMemoryDb.values().stream()
                    .filter(user -> user.getUserName().contains(userName))
                    .collect(Collectors.toList());
        }
    }

    // 특정 사용자 ID로 조회 (SELECT by ID)
    public UserDto selectUserById(String userId) {
        System.out.println("[DAO] selectUserById 호출. ID: " + userId);
        return inMemoryDb.get(userId);
    }

    // 사용자 정보 삽입 (INSERT)
    public void insertUser(UserDto userDto) {
        System.out.println("[DAO] insertUser 호출. 삽입할 사용자: " + userDto.toString());
        if (inMemoryDb.containsKey(userDto.getUserId())) {
            throw new RuntimeException("이미 존재하는 사용자 ID입니다: " + userDto.getUserId());
        }
        inMemoryDb.put(userDto.getUserId(), userDto);
        System.out.println("[DAO] 사용자 삽입 완료. 현재 총 사용자 수: " + inMemoryDb.size());
    }

    // 사용자 정보 수정 (UPDATE)
    public void updateUser(UserDto userDto) {
        System.out.println("[DAO] updateUser 호출. 수정할 사용자: " + userDto.toString());
        if (inMemoryDb.containsKey(userDto.getUserId())) {
            inMemoryDb.put(userDto.getUserId(), userDto); // 기존 데이터 덮어쓰기
            System.out.println("[DAO] 사용자 수정 완료.");
        } else {
            throw new RuntimeException("존재하지 않는 사용자 ID입니다: " + userDto.getUserId());
        }
    }

    // 사용자 정보 삭제 (DELETE)
    public void deleteUser(String userId) {
        System.out.println("[DAO] deleteUser 호출. 삭제할 ID: " + userId);
        if (inMemoryDb.containsKey(userId)) {
            inMemoryDb.remove(userId);
            System.out.println("[DAO] 사용자 삭제 완료. 현재 총 사용자 수: " + inMemoryDb.size());
        } else {
            throw new RuntimeException("존재하지 않는 사용자 ID입니다: " + userId);
        }
    }
}

소스 분석 및 흐름 파악 방법

위 예제 소스를 가지고 직접 IDE에서 프로젝트를 생성하여 실행해보면서 다음 순서로 분석하세요.

  1. 프로젝트 설정:
    • Spring Boot 프로젝트를 생성합니다 (Java 11 이상 권장).
    • spring-boot-starter-web 의존성을 pom.xml에 추가합니다.
    • 위의 UserDto, UserController, UserService, UserDao 클래스 파일을 적절한 패키지(com.example.websquareexample.dto, .controller, .service, .dao 등)에 생성합니다.
    • Spring Boot 애플리케이션을 실행합니다. (main 메서드가 있는 @SpringBootApplication 클래스 실행)
  2. WebSquare 파일 배치:
    • WebSquare 프로젝트 내에 userList.xml과 userList.js 파일을 적절한 경로(예: ui/user/userList.xml, ui/user/userList.js)에 배치하고 WebSquare 서버를 통해 화면을 띄웁니다.
  3. 흐름 따라가기 (예: 사용자 목록 조회 - READ)
    • WebSquare (userList.xml, userList.js):
      • 브라우저에서 userList.xml 화면을 엽니다.
      • btn_search 버튼의 onClick 이벤트 (scwin.btn_search_onClick)를 확인합니다.
      • scwin.btn_search_onClick 함수 내에서 scwin.searchUserList()를 호출하는 것을 확인합니다.
      • scwin.searchUserList 함수에서 WebSquare.request.execute() 호출을 찾습니다.
      • 여기서 action : "/api/users", method : "GET", requestData : searchParam (dataMap_search의 userName)을 확인합니다.
      • successCallback에서 dataList_userList.setJSON(res.responseJSON.userList)를 통해 서버 응답 중 userList라는 키의 값을 dataList_userList에 바인딩하는 것을 확인합니다.
    • Spring Backend (UserController.java):
      • UserController.java를 열고 @GetMapping("/users") 어노테이션이 붙은 getUsers 메서드를 찾습니다.
      • 메서드의 파라미터 @RequestParam(required = false) String userName을 통해 WebSquare에서 보낸 userName이 여기에 매핑됨을 확인합니다.
      • userService.getUsers(userName)를 호출하여 서비스 계층으로 제어권을 넘기는 것을 확인합니다.
      • 반환된 userList를 Map에 userList라는 키로 담아 ResponseEntity로 반환하는 것을 확인합니다. (이것이 WebSquare successCallback의 res.responseJSON.userList에 해당)
    • Spring Backend (UserService.java):
      • UserService.java를 열고 getUsers(String userName) 메서드를 찾습니다.
      • 여기서 userDao.selectUsers(userName)를 호출하여 DAO 계층으로 제어권을 넘기는 것을 확인합니다.
    • Spring Backend (UserDao.java):
      • UserDao.java를 열고 selectUsers(String userName) 메서드를 찾습니다.
      • 여기서는 inMemoryDb에서 userName에 따라 필터링하여 UserDto 리스트를 반환하는 "데이터 조회" 로직을 확인합니다.
  4. 흐름 따라가기 (예: 사용자 등록 - CREATE)
    • WebSquare (userList.xml, userList.js):
      • btn_new 클릭 -> popup_userReg 열림 -> dataMap_user에 아이디, 이름, 이메일 입력.
      • btn_save 클릭 -> scwin.btn_save_onClick 호출.
      • dataMap_user.getJSON()으로 입력된 데이터를 userData 변수에 저장.
      • WebSquare.request.execute() 호출에서 action : "/api/users", method : "POST", requestData : userData를 확인합니다.
    • Spring Backend (UserController.java):
      • UserController.java를 열고 @PostMapping("/users") 어노테이션이 붙은 createUser 메서드를 찾습니다.
      • 메서드의 파라미터 @RequestBody UserDto userDto를 통해 WebSquare에서 보낸 JSON 데이터가 UserDto 객체로 매핑되는 것을 확인합니다.
      • userService.createUser(userDto)를 호출하여 서비스 계층으로 제어권을 넘기는 것을 확인합니다.
      • 성공 시 HttpStatus.CREATED (201)를 반환하는 것을 확인합니다.
    • Spring Backend (UserService.java):
      • UserService.java를 열고 createUser(UserDto userDto) 메서드를 찾습니다.
      • @Transactional 어노테이션으로 트랜잭션이 적용됨을 확인합니다.
      • userDao.selectUserById()로 ID 중복 확인 후 userDao.insertUser(userDto)를 호출하여 DAO 계층으로 제어권을 넘기는 것을 확인합니다.
    • Spring Backend (UserDao.java):
      • UserDao.java를 열고 insertUser(UserDto userDto) 메서드를 찾습니다.
      • 여기서는 inMemoryDb에 UserDto 객체를 추가하는 "데이터 삽입" 로직을 확인합니다.

clo.omn 파일 관련

이 예제에서는 clo.omn 파일에 대한 실제 구현은 포함하지 않습니다. 하지만 만약 프로젝트에 해당 파일이 있다면:

  1. 파일 열기: 해당 파일을 텍스트 에디터로 열어서 내부 구조(XML, JSON 등)와 키워드를 확인합니다.
  2. 전역 검색: 백엔드 Java 코드(*.java)와 WebSquare JavaScript (*.js)에서 clo.omn 문자열을 전역 검색합니다. 어떤 코드에서 이 파일을 읽고 파싱하는지, 그리고 어떤 목적으로 사용하는지 (Clova, chatbot, OCR 관련 로직)를 찾아내면 파일의 역할을 정확히 파악할 수 있습니다.

이 간단한 예제를 통해 WebSquare와 백엔드 간의 데이터 흐름, 그리고 컨트롤러-서비스-DAO 계층 간의 역할 분담을 명확히 이해하실 수 있을 겁니다. 직접 코드를 실행하고 디버깅해보는 것이 가장 중요합니다!

 

스프링 부트 (STS)를 이용한 학생 정보 관리 시스템 구축 예제

1. 프로젝트 설정

  • **STS (Spring Tool Suite)**를 실행하고 새로운 Spring Boot 프로젝트를 생성합니다.
  • 프로젝트 이름: StudentManagement
  • 패키지 이름: com.example.student
  • 의존성: Spring Web, Spring Data JPA, H2 Database, MyBatis Framework

2. VO (Value Object) 클래스 생성

  • Student.java
Java
 
package com.example.student.vo;

public class Student {
    private int id; // 학번
    private String name; // 이름
    private String phone; // 전화번호

    // 생성자, Getter/Setter
}

3. Mapper 인터페이스 생성

  • StudentMapper.java
Java
 
package com.example.student.mapper;

import com.example.student.vo.Student;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface StudentMapper {
    List<Student> selectAllStudents();
    Student selectStudentById(int id);
    void insertStudent(Student student);
    void updateStudent(Student student);
    void deleteStudent(int id);
}

4. Service 클래스 생성

  • StudentService.java
Java
 
package com.example.student.service;

import com.example.student.mapper.StudentMapper;
import com.example.student.vo.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

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

    public List<Student> getAllStudents() {
        return studentMapper.selectAllStudents();
    }

    public Student getStudentById(int id) {
        return studentMapper.selectStudentById(id);
    }

    public void createStudent(Student student) {
        studentMapper.insertStudent(student);
    }

    public void updateStudent(Student student) {
        studentMapper.updateStudent(student);
    }

    public void deleteStudent(int id) {
        studentMapper.deleteStudent(id);
    }
}

5. Controller 클래스 생성

  • StudentController.java
Java
 
package com.example.student.controller;

import com.example.student.service.StudentService;
import com.example.student.vo.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/students")
public class StudentController {
    @Autowired
    private StudentService studentService;

    @GetMapping
    public List<Student> getAllStudents() {
        return studentService.getAllStudents();
    }

    @GetMapping("/{id}")
    public Student getStudentById(@PathVariable int id) {
        return studentService.getStudentById(id);
    }

    @PostMapping
    public void createStudent(@RequestBody Student student) {
        studentService.createStudent(student);
    }

    @PutMapping("/{id}")
    public void updateStudent(@PathVariable int id, @RequestBody Student student) {
        student.setId(id);
        studentService.updateStudent(student);
    }

    @DeleteMapping("/{id}")
    public void deleteStudent(@PathVariable int id) {
        studentService.deleteStudent(id);
    }
}

6. 데이터베이스 설정

  • application.properties
Properties
 
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=create
spring.h2.console.enabled=true

7. MyBatis 설정

  • application.properties
Properties
 
mybatis.configuration.map-underscore-to-camel-case=true

8. 실행

  • STS에서 프로젝트를 실행합니다.
  • http://localhost:8080/h2-console에 접속하여 H2 콘솔을 확인합니다.
  • Postman 또는 브라우저를 이용하여 API를 테스트합니다.

예제 실행 및 API 호출 방법

  1. 학생 목록 조회: GET /students
  2. 학생 상세 조회: GET /students/{id}
  3. 학생 추가: POST /students (Request Body에 학생 정보 JSON 형식으로 전달)
  4. 학생 수정: PUT /students/{id} (Request Body에 수정할 학생 정보 JSON 형식으로 전달)
  5. 학생 삭제: DELETE /students/{id}

 

H2 데이터베이스 생성 및 데이터 입력 방법

1. H2 데이터베이스 설정 확인

  • application.properties 파일에 다음 설정이 추가되어 있는지 확인합니다.
Properties
 
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=create
spring.h2.console.enabled=true
  • spring.datasource.url은 H2 데이터베이스의 URL을 설정합니다. jdbc:h2:mem:testdb는 메모리 기반 데이터베이스를 사용하며, 애플리케이션이 종료되면 데이터가 사라집니다. 파일 기반 데이터베이스를 사용하려면 jdbc:h2:file:./testdb와 같이 설정할 수 있습니다.
  • spring.jpa.hibernate.ddl-auto=create는 JPA가 자동으로 데이터베이스 스키마를 생성하도록 설정합니다.
  • spring.h2.console.enabled=true는 H2 콘솔을 활성화합니다.

2. 애플리케이션 실행

  • STS에서 프로젝트를 실행합니다.

3. H2 콘솔 접속

  • 웹 브라우저를 열고 http://localhost:8080/h2-console에 접속합니다.
  • JDBC URL, 사용자 이름, 비밀번호를 입력하고 "Connect" 버튼을 클릭합니다. (기본 설정인 경우 JDBC URL은 jdbc:h2:mem:testdb, 사용자 이름은 sa, 비밀번호는 비어있는 상태로 접속 가능합니다.)

4. 테이블 생성

  • H2 콘솔에서 SQL 쿼리를 실행하여 테이블을 생성합니다. 예를 들어, 학생 정보를 저장하는 student 테이블을 생성하려면 다음 쿼리를 실행합니다.
SQL
 
CREATE TABLE student (
    id INT PRIMARY KEY,
    name VARCHAR(255),
    phone VARCHAR(20)
);

5. 데이터 입력

  • H2 콘솔에서 SQL 쿼리를 실행하여 데이터를 입력합니다. 예를 들어, 세 명의 학생 정보를 입력하려면 다음 쿼리를 실행합니다.
SQL
 
INSERT INTO student (id, name, phone) VALUES
(1, '홍길동', '010-1234-5678'),
(2, '김철수', '010-9876-5432'),
(3, '박영희', '010-1111-2222');

6. 데이터 확인

  • H2 콘솔에서 SQL 쿼리를 실행하여 데이터를 확인합니다. 예를 들어, student 테이블의 모든 데이터를 조회하려면 다음 쿼리를 실행합니다.
SQL
 
SELECT * FROM student;

주의 사항

  • H2 데이터베이스는 개발 및 테스트 환경에서 주로 사용됩니다. 운영 환경에서는 다른 데이터베이스를 사용하는 것이 좋습니다.
  • 메모리 기반 데이터베이스를 사용하는 경우, 애플리케이션이 종료되면 데이터가 사라지므로 주의해야 합니다.

위 단계를 따르면 H2 데이터베이스를 생성하고 데이터를 입력할 수 있습니다. 

 

StudentMapper.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="com.example.student.mapper.StudentMapper">

    <select id="selectAllStudents" resultType="com.example.student.vo.Student">
        SELECT id, name, phone FROM student
    </select>

    <select id="selectStudentById" parameterType="int" resultType="com.example.student.vo.Student">
        SELECT id, name, phone FROM student WHERE id = #{id}
    </select>

    <insert id="insertStudent" parameterType="com.example.student.vo.Student">
        INSERT INTO student (id, name, phone) VALUES (#{id}, #{name}, #{phone})
    </insert>

    <update id="updateStudent" parameterType="com.example.student.vo.Student">
        UPDATE student SET name = #{name}, phone = #{phone} WHERE id = #{id}
    </update>

    <delete id="deleteStudent" parameterType="int">
        DELETE FROM student WHERE id = #{id}
    </delete>

</mapper>

설명:

  • <mapper namespace="com.example.student.mapper.StudentMapper">: namespace 속성은 Mapper 인터페이스의 패키지 경로와 클래스 이름을 지정합니다. 중요합니다.
  • <select id="selectAllStudents" resultType="com.example.student.vo.Student">:
    • id: Mapper 인터페이스의 메서드 이름과 일치해야 합니다.
    • resultType: 쿼리 결과로 반환될 객체의 타입입니다. com.example.student.vo.Student는 VO 클래스의 전체 경로를 나타냅니다.
    • SELECT id, name, phone FROM student: 실행할 SQL 쿼리입니다.
  • <select id="selectStudentById" parameterType="int" resultType="com.example.student.vo.Student">:
    • parameterType: 쿼리 실행 시 전달될 파라미터의 타입입니다. int는 정수형 파라미터를 의미합니다.
    • WHERE id = #{id}: #{id}는 파라미터 값을 SQL 쿼리에 바인딩합니다.
  • <insert id="insertStudent" parameterType="com.example.student.vo.Student">:
    • parameterType: 삽입할 데이터를 담고 있는 객체의 타입입니다.
  • <update id="updateStudent" parameterType="com.example.student.vo.Student">:
    • parameterType: 수정할 데이터를 담고 있는 객체의 타입입니다.
  • <delete id="deleteStudent" parameterType="int">:
    • parameterType: 삭제할 데이터의 ID 타입을 나타냅니다.

중요 사항:

  • 파일 위치: 위 XML 파일은 src/main/resources/mappers 폴더에 저장해야 합니다. application.properties 또는 application.yml 파일에 mybatis.mapper-locations=classpath:mappers/*.xml 설정을 추가하여 MyBatis가 Mapper XML 파일을 찾을 수 있도록 해야 합니다.
  • 이름 규칙: Mapper XML 파일 이름은 Mapper 인터페이스 이름과 동일해야 합니다. (예: StudentMapper.xml)
  • 오류 방지: 오타나 잘못된 SQL 구문이 없는지 꼼꼼히 확인하세요.

사용 방법:

  1. 위 XML 코드를 src/main/resources/mappers/StudentMapper.xml 파일에 저장합니다.
  2. application.properties 또는 application.yml 파일에 MyBatis 설정 (mybatis.mapper-locations)이 올바르게 되어 있는지 확인합니다.
  3. 프로젝트를 다시 빌드하고 실행합니다.

이제 StudentMapper 인터페이스를 통해 데이터베이스 작업을 수행할 수 있습니다. 예를 들어, studentMapper.selectAllStudents()를 호출하면 student 테이블의 모든 데이터를 조회하여 List<Student> 객체로 반환합니다.

 

 

Spring MVC Controller, Service, Mapper (MyBatis) 예제: DB 데이터 View 페이지로 넘기기

아래는 Spring MVC 패턴을 사용하여 DB에서 데이터를 조회하여 View 페이지로 넘기는 예제입니다. MyBatis를 ORM 프레임워크로 사용하며, Controller, Service, Mapper (DAO 역할) 클래스를 포함합니다.

1. Controller (MaCinkRomngController.java)

Java
 
@Controller
@RequestMapping(FOLDER_PATH) // FOLDER_PATH는 클래스 상단에 상수로 정의
public class MaCinkRomngController {

    @Autowired
    private MaCinkRomngService maCinkRomngService;

    @RequestMapping("list.do")
    public String list(@ModelAttribute("searchVO") MaCinkRomngVO searchVO, Model model) {
        // 1. Service 객체를 통해 DB 데이터 조회
        List<MaCinkRomngVO> list = maCinkRomngService.selectMaCinkRomngList(searchVO);

        // 2. Model 객체에 조회된 데이터 추가
        model.addAttribute("resultList", list); // "resultList" 이름으로 view에서 사용

        return ".mLayout" + FOLDER_PATH + "list"; // view 페이지 경로 반환
    }
}

2. Service (MaCinkRomngService.java)

Java
 
@Service
public class MaCinkRomngService {

    @Autowired
    private MaCinkRomngMapper maCinkRomngMapper;

    public List<MaCinkRomngVO> selectMaCinkRomngList(MaCinkRomngVO searchVO) {
        return maCinkRomngMapper.selectMaCinkRomngList(searchVO);
    }
}

3. Mapper (MaCinkRomngMapper.java)

Java
 
public interface MaCinkRomngMapper {
    List<MaCinkRomngVO> selectMaCinkRomngList(MaCinkRomngVO searchVO);
}

4. MyBatis Mapper XML (maCinkRomngMapper.xml)

XML
 
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="kepco.evc.ma.mbrMng.persnMbr.clnkRomng.mapper.MaCinkRomngMapper">
    <select id="selectMaCinkRomngList" parameterType="MaCinkRomngVO" resultType="MaCinkRomngVO">
        SELECT /* 필요한 컬럼 목록 */
        FROM /* 테이블 이름 */
        WHERE 1=1
        <if test="searchWord != null and searchWord != ''">
            AND /* 검색 조건 컬럼 */ LIKE concat('%', #{searchWord}, '%')
        </if>
        <if test="searchType != null and searchType != ''">
            AND /* 검색 조건 컬럼 */ = #{searchType}
        </if>
        /* 추가 검색 조건 */
        ORDER BY /* 정렬 기준 컬럼 */ DESC
    </select>
</mapper>

5. View Page (list.jsp)

Java
 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<table>
    <thead>
        <tr>
            <th>회원번호</th>
            <th>이름</th>
            <th>...</th>
        </tr>
    </thead>
    <tbody>
        <c:forEach var="item" items="${resultList}">
            <tr>
                <td>${item.mbrNo}</td>
                <td>${item.name}</td>
                <td>...</td>
            </tr>
        </c:forEach>
    </tbody>
</table>

주요 설명:

  • Controller: list() 메서드는 Service를 통해 DB 데이터를 조회하고, Model 객체에 데이터를 담아 view 페이지로 전달합니다.
  • Service: selectMaCinkRomngList() 메서드는 Mapper를 호출하여 DB 데이터를 조회합니다.
  • Mapper: selectMaCinkRomngList() 메서드는 MyBatis를 사용하여 SQL 쿼리를 실행하고, 결과를 MaCinkRomngVO 객체 리스트로 반환합니다.
  • MyBatis Mapper XML: select 태그는 SQL 쿼리를 정의하며, parameterType은 파라미터 타입, resultType은 결과값을 나타냅니다.
  • View Page: ${resultList} EL 표현식을 사용하여 Model 객체에 담긴 데이터를 출력합니다. JSTL의 c:forEach 태그를 사용하여 리스트를 반복하며 데이터를 표시합니다.

 

ObjectMapper 매퍼 = new ObjectMapper(); 설명

ObjectMapper는 Jackson 라이브러리에서 제공하는 클래스로, Java 객체와 JSON 데이터 간의 변환을 담당합니다. 즉, Java 객체를 JSON 문자열로 변환하거나, JSON 문자열을 Java 객체로 변환하는 작업을 수행합니다.

ObjectMapper의 역할

  • 직렬화 (Serialization): Java 객체를 JSON 형식의 문자열로 변환하는 과정입니다.
  • 역직렬화 (Deserialization): JSON 형식의 문자열을 Java 객체로 변환하는 과정입니다.

코드 설명

자바
 
ObjectMapper mapper = new ObjectMapper();
  • ObjectMapper mapper: ObjectMapper 객체를 생성하여 mapper 변수에 할당합니다. 이 객체를 통해 다양한 직렬화/역직렬화 작업을 수행할 수 있습니다.
  • new ObjectMapper(): ObjectMapper 클래스의 기본 생성자를 호출하여 새로운 ObjectMapper 객체를 생성합니다.

ObjectMapper의 주요 기능

  • 객체를 JSON으로 변환: writeValueAsString() 메소드를 사용하여 Java 객체를 JSON 문자열로 변환합니다.
  • JSON을 객체로 변환: readValue() 메소드를 사용하여 JSON 문자열을 Java 객체로 변환합니다.
  • 커스터마이징: 다양한 설정을 통해 직렬화/역직렬화 과정을 커스터마이징할 수 있습니다. 예를 들어, 날짜 형식, null 값 처리, 필드 이름 변경 등을 설정할 수 있습니다.

예시

자바
 
import com.fasterxml.jackson.databind.ObjectMapper;

class Person {
    private String name;
    private int age;
    // getter, setter 생략
}

public class Main {
    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        
        Person person = new Person();
        person.setName("홍길동");
        person.setAge(30);

        // 객체를 JSON으로 변환
        String json = mapper.writeValueAsString(person);
        System.out.println(json); // 출력: {"name":"홍길동","age":30}

        // JSON을 객체로 변환
        Person person2 = mapper.readValue(json, Person.class);
        System.out.println(person2.getName()); // 출력: 홍길동
    }
}

추가 설명

  • Jackson 라이브러리: Java에서 JSON 처리를 위한 가장 많이 사용되는 라이브러리 중 하나입니다. ObjectMapper 외에도 다양한 유용한 클래스와 메소드를 제공합니다.
  • JSON: 자바스크립트 객체 표기법(JavaScript Object Notation)의 약자로, 데이터를 간결하고 가볍게 표현하는 데 사용되는 텍스트 기반 데이터 교환 형식입니다.
  • 직렬화/역직렬화: 객체를 데이터 스트림으로 변환하는 과정을 직렬화, 데이터 스트림을 객체로 변환하는 과정을 역직렬화라고 합니다.

ObjectMapper는 Java와 JSON 간의 데이터 교환을 효율적으로 처리하기 위한 필수적인 도구입니다. 다양한 설정을 통해 필요에 맞게 사용할 수 있습니다.

 

패키지에 impl을 붙이는 이유

패키지 이름에 impl을 붙이는 것은 일반적으로 인터페이스의 구현 클래스를 담는 패키지를 나타낼 때 사용하는 관습적인 명명법입니다.

왜 impl을 사용할까요?

  • 명확한 구분: 인터페이스와 구현 클래스를 명확하게 구분하여 코드의 가독성을 높입니다.
  • 구조화: 프로젝트의 구조를 체계적으로 관리하고 유지보수를 용이하게 합니다.
  • 협업: 다른 개발자들이 프로젝트 구조를 빠르게 이해하고 협업할 수 있도록 돕습니다.

예시

자바
 
// 인터페이스 패키지
com.example.service
// 구현 클래스 패키지
com.example.service.impl

위 예시에서 com.example.service 패키지에는 UserService와 같은 인터페이스가, com.example.service.impl 패키지에는 UserServiceImpl과 같은 구현 클래스가 위치하게 됩니다.

impl을 사용하는 이유에 대한 더 자세한 설명

  • 인터페이스와 구현의 분리: 인터페이스는 시스템이 제공해야 하는 기능을 정의하고, 구현 클래스는 실제 기능을 구현하는 역할을 합니다. 이를 명확하게 분리하여 코드의 결합도를 낮추고 유연성을 높일 수 있습니다.
  • 다형성 지원: 인터페이스를 통해 다형성을 구현할 수 있습니다. 즉, 같은 인터페이스를 구현하는 다양한 구현 클래스를 사용하여 동일한 메소드를 호출하더라도 다른 결과를 얻을 수 있습니다.
  • 테스트 용이성: 인터페이스를 통해 모킹 객체를 생성하여 테스트를 수행할 수 있습니다. 이는 시스템의 특정 부분을 독립적으로 테스트하는 데 유용합니다.
  • 의존성 주입: 스프링 프레임워크와 같은 DI 컨테이너를 사용하여 인터페이스를 통해 의존성을 주입할 수 있습니다. 이는 시스템의 유연성을 높이고 테스트를 용이하게 합니다.

결론

패키지에 impl을 붙이는 것은 코드의 가독성, 유지보수성, 그리고 확장성을 높이는 데 기여하는 좋은 관습입니다. 특히 대규모 프로젝트나 여러 개발자가 참여하는 프로젝트에서 더욱 중요한 의미를 가집니다.

하지만 반드시 impl을 사용해야 하는 것은 아닙니다. 프로젝트의 규모나 특성에 따라 다른 명명 규칙을 사용할 수도 있습니다. 중요한 것은 일관된 명명 규칙을 정하고 팀원들과 공유하는 것입니다.

REST 방식에서 쿼리스트링이란 무엇일까요? 쉽게 설명해 드릴게요!

쿼리스트링(Query String)이란, 웹 주소(URL)의 끝에 물음표(?) 뒤에 붙어서 추가적인 정보를 전달하는 것을 말해요. 마치 편지를 부를 때 봉투에 받는 사람의 주소 외에, 추가로 전달하고 싶은 메시지를 적는 것과 비슷하다고 생각하면 돼요.

왜 쿼리스트링을 사용할까요?

  • 데이터 전달: 서버에 특정 데이터를 전달하여 원하는 정보를 얻을 수 있어요. 예를 들어, 온라인 쇼핑몰에서 검색을 할 때 검색어를 쿼리스트링으로 전달하여 검색 결과를 받아오는 것이죠.
  • 조건 설정: 데이터를 조회할 때 다양한 조건을 설정할 수 있어요. 예를 들어, 게시판에서 글 목록을 조회할 때 페이지 번호, 정렬 기준 등을 쿼리스트링으로 지정할 수 있죠.

쿼리스트링의 구조

쿼리스트링은 key=value 형태로 구성되며, 여러 개의 키-값 쌍은 &로 연결됩니다.

  • key: 전달하는 데이터의 종류를 나타내는 이름
  • value: key에 해당하는 값

예시:

https://www.example.com/search?query=스프링&page=2
  • query: 검색어를 의미하는 키
  • 스프링: 검색어 값
  • page: 페이지 번호를 의미하는 키
  • 2: 페이지 번호 값

위 예시는 "스프링"이라는 키워드로 검색하고, 검색 결과의 두 번째 페이지를 보여달라는 요청입니다.

쿼리스트링의 장점

  • 간단하고 직관적: URL에 직접 추가하여 사용하기 때문에 이해하기 쉽습니다.
  • 다양한 데이터 전달: 숫자, 문자열 등 다양한 형태의 데이터를 전달할 수 있습니다.
  • HTTP GET 메서드와 주로 사용: GET 메서드는 데이터를 조회하는 용도로 많이 사용되며, 쿼리스트링과 자주 함께 사용됩니다.

쿼리스트링의 단점

  • 보안: 쿼리스트링은 URL에 노출되기 때문에 민감한 정보를 전달하는 데는 적합하지 않습니다. (예: 비밀번호)
  • 데이터 크기 제한: 일부 브라우저나 서버에서는 쿼리스트링의 길이에 제한이 있을 수 있습니다.
  • 복잡한 데이터 전달: 복잡한 데이터를 전달하기에는 적합하지 않을 수 있습니다. 이 경우에는 HTTP 요청 본문에 JSON 또는 XML 형식으로 데이터를 담아 전달하는 것이 더 적합합니다.

결론적으로, 쿼리스트링은 간단한 데이터를 전달하고, URL을 통해 직관적으로 요청을 표현할 수 있기 때문에 REST API에서 자주 사용되는 방법입니다. 하지만 보안이나 데이터 크기 등의 제약 사항을 고려하여 적절하게 사용해야 합니다.

스프링에서 모델1, 모델2 방식을 초등학생도 이해하도록 쉽게 설명해 줄게!

피자 만들기로 비유해 볼까요?

모델1은 마치 혼자서 피자를 다 만드는 친구와 같아요. 밀가루 반죽부터 토핑 올리고, 오븐에 넣어 굽기까지 모든 과정을 한 사람이 다 하는 거죠.

  • 장점: 간단하고 빠르게 만들 수 있어요.
  • 단점: 만약 피자 레시피가 복잡해지거나 여러 사람이 함께 만들고 싶을 때는 혼란스럽고 비효율적일 수 있어요.

모델2여러 친구가 협력해서 피자를 만드는 모습과 비슷해요.

  • 반죽 만드는 친구: 밀가루 반죽을 만드는 역할 (Model)
  • 토핑 올리는 친구: 피자에 토핑을 올리는 역할 (View)
  • 오븐에 넣는 친구: 오븐에 피자를 넣고 굽는 역할 (Controller)

각자 맡은 일에 집중하기 때문에 더 깔끔하고 효율적으로 피자를 만들 수 있어요.

스프링에서 모델1, 모델2가 뭐냐고?

  • 모델1: JSP 파일 하나로 모든 일을 처리하는 방식이에요. 웹 페이지를 만들고, 데이터를 처리하고, 사용자의 요청을 받는 모든 역할을 JSP 파일이 다 해요.
  • 모델2: JSP, Servlet, JavaBean을 나눠서 사용하는 방식이에요. JSP는 화면을 보여주는 역할만 하고, Servlet은 사용자의 요청을 받아 처리하고, JavaBean은 데이터를 관리하는 역할을 해요.

왜 모델2를 더 많이 사용할까?

  • 유지보수가 쉽다: 각 파일의 역할이 명확해서 코드를 수정하거나 추가하기가 쉬워요.
  • 확장성이 좋다: 새로운 기능을 추가하거나 복잡한 웹 애플리케이션을 만들 때 유연하게 대처할 수 있어요.
  • 팀 개발에 적합하다: 여러 개발자가 각자 맡은 부분을 개발할 수 있어서 협업이 용이해요.

정리하면

  • 모델1: 간단한 웹 페이지를 만들 때 유용하지만, 복잡한 시스템에는 적합하지 않아요.
  • 모델2: 대규모 웹 애플리케이션 개발에 적합하고, 유지보수와 확장성이 좋지만, 초기 설정이 조금 복잡할 수 있어요.

스프링 프레임워크는 모델2 방식을 기반으로 만들어졌기 때문에, 스프링을 사용하면 복잡한 웹 애플리케이션도 효율적으로 개발할 수 있어요.

 

스프링 프레임워크와 커피 제조: 더 깊이 알아보기

스프링 프레임워크커피 제조에 비유했을 때, 다음과 같이 더 자세히 설명할 수 있습니다.

  • 커피 콩, 물, 커피 머신 (의존성): 스프링에서 의존성은 마치 커피를 만들기 위해 필요한 커피 콩, 물, 커피 머신과 같습니다. 어떤 기능을 수행하기 위해 반드시 필요한 다른 객체나 자원을 의미합니다.
  • 바리스타 (스프링 컨테이너): 스프링 컨테이너바리스타와 같습니다. 바리스타는 커피 콩, 물, 커피 머신을 준비하고, 이들을 이용하여 맛있는 커피를 만들어냅니다. 마찬가지로, 스프링 컨테이너는 개발자가 정의한 객체들을 관리하고, 이들을 연결하여 애플리케이션을 실행시킵니다.
  • 커피 레시피 (스프링 설정): 스프링 설정커피 레시피와 같습니다. 어떤 재료를 얼마나 사용하고, 어떤 순서로 작업을 진행할지에 대한 상세한 정보를 담고 있습니다. 마찬가지로, 스프링 설정 파일(XML, Java Config 등)에는 어떤 객체를 생성하고, 어떻게 연결할지에 대한 정보가 담겨 있습니다.
  • 창고 (스프링 빈 컨테이너): 스프링 빈 컨테이너는 커피 재료를 보관하는 창고와 같습니다. 스프링 컨테이너는 생성된 객체들을 저장하고 관리하며, 필요할 때마다 꺼내서 사용합니다.

핵심은 의존성 주입입니다. 바리스타가 커피 콩, 물, 커피 머신을 직접 구매하는 대신, 창고에서 가져다 쓰는 것처럼, 스프링 컨테이너는 객체를 직접 생성하는 대신, 이미 생성되어 있는 객체를 주입하여 사용합니다. 이렇게 함으로써 코드의 결합도를 낮추고, 유연성을 높일 수 있습니다.

좀 더 구체적인 예시

  • 커피 주문: 사용자가 커피를 주문하면, 스프링 컨테이너는 주문 정보를 바탕으로 적절한 커피 레시피를 찾고, 필요한 재료(객체)를 빈 컨테이너에서 가져와 커피를 만들어줍니다.
  • 우유 추가: 커피에 우유를 추가하고 싶다면, 스프링 설정 파일에서 우유 객체를 추가하고, 커피를 만드는 과정에 우유를 넣도록 설정하면 됩니다.

정리

스프링 프레임워크는 객체 간의 의존 관계를 관리하고, 객체를 생성하며, 이들을 연결하여 애플리케이션을 실행하는 프레임워크입니다. 커피 제조에 비유하여 설명하면, 스프링은 바리스타가 되어 다양한 커피 레시피를 만들고, 커피 콩, 물, 커피 머신과 같은 재료를 관리하는 역할을 합니다.

 

레고 블록으로 만드는 나만의 커피 기계!

스프링 프레임워크는 우리가 좋아하는 레고 블록을 가지고 커피 기계를 만드는 것과 비슷해요. 레고 블록처럼 다양한 기능을 가진 작은 조각들을 연결해서 커피를 만들어주는 기계를 만들 수 있지요.

커피 주문하기

  1. 주문하기: 친구에게 "아메리카노 한 잔 주세요!"라고 말하는 것처럼, 컴퓨터에 "아메리카노 주세요!"라고 명령을 내리면 스프링이라는 친구가 우리의 주문을 받아요.
  2. 레시피 찾기: 스프링이는 우리가 주문한 아메리카노를 만들기 위한 레시피를 찾아봐요. 레시피에는 어떤 재료(레고 블록)가 필요하고, 어떤 순서로 조립해야 하는지가 자세히 적혀있어요.
  3. 재료 준비하기: 레시피에 따라 필요한 재료(레고 블록)들을 찾아서 준비해요. 예를 들어, 아메리카노를 만들기 위해서는 커피 콩, 물, 필터 등이 필요하겠죠?
  4. 조립하기: 준비된 재료들을 레시피에 따라 조립해요. 마치 레고 블록을 연결하듯이, 스프링이는 각각의 재료들을 연결해서 아메리카노를 만들 수 있는 커피 기계를 만들어요.
  5. 커피 마시기: 드디어 완성된 커피 기계를 이용해서 맛있는 아메리카노를 마실 수 있어요!

우유 추가하기

만약 우리가 라떼를 마시고 싶다면, 레시피에 우유를 추가하는 부분만 바꿔주면 돼요. 마치 레고 기계에 우유를 넣는 부분을 추가하는 것처럼 말이죠. 스프링은 우리가 바꾼 레시피대로 새로운 커피 기계를 만들어줄 거예요.

왜 스프링이 필요할까?

  • 복잡한 커피 기계도 쉽게 만들 수 있어요: 스프링이라는 똑똑한 친구 덕분에 우리는 복잡한 커피 기계도 레고 블록을 조립하듯 쉽게 만들 수 있어요.
  • 다양한 커피를 만들 수 있어요: 레시피만 바꾸면 아메리카노, 라떼, 카푸치노 등 다양한 커피를 만들 수 있어요.
  • 새로운 기능을 추가하기 쉬워요: 레고 블록을 추가하듯이, 새로운 기능을 쉽게 추가할 수 있어요. 예를 들어, 에스프레소 샷을 추가하거나, 우유 거품을 내는 기능을 추가할 수 있죠.

정리

스프링 프레임워크는 우리가 컴퓨터 프로그램을 만들 때, 마치 레고 블록을 가지고 놀듯이 쉽고 재미있게 프로그램을 만들 수 있도록 도와주는 도구예요. 스프링이라는 똑똑한 친구 덕분에 우리는 다양한 기능을 가진 프로그램을 만들 수 있고, 프로그램을 수정하거나 새로운 기능을 추가하는 것도 쉽게 할 수 있답니다.

 

스프링 프레임워크 기본 예제: 커피 주문 시스템

1. 프로젝트 생성 및 의존성 추가

  • 스프링 부트 프로젝트를 생성합니다.
  • pom.xml에 스프링 부트 웹 스타터 의존성을 추가합니다.
XML
 
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

2. 커피 인터페이스 정의

Java
 
public interface Coffee {
    String getCoffeeName();
}

3. 커피 구현체 (아메리카노)

Java
 
@Componet
public class Americano implements Coffee {
    @Override
    public String getCoffeeName() {
        return "아메리카노";
    }
}

4. 커피 주문 서비스

Java
 
@Service
public class CoffeeOrderService {
    @Autowired
    private Americano americano;

    public String orderCoffee(String coffeeName) {
        if ("아메리카노".equals(coffeeName)) {
            return americano.getCoffeeName() + " 주문 완료!";
        } else {
            return "지원하지 않는 메뉴입니다.";
        }
    }
}

5. REST API 생성

Java
 
@RestController
public class CoffeeController {
    @Autowired
    private CoffeeOrderService coffeeOrderService;

    @GetMapping("/coffee/{coffeeName}")
    public String orderCoffee(@PathVariable String coffeeName) {
        return coffeeOrderService.orderCoffee(coffeeName);
    }
}

코드 설명

  • Coffee 인터페이스: 커피의 공통적인 메서드를 정의합니다.
  • Americano 클래스: Coffee 인터페이스를 구현하여 아메리카노를 나타냅니다.
  • CoffeeOrderService: 커피 주문 로직을 담당합니다. @Autowired를 사용하여 Americano 객체를 주입받습니다.
  • CoffeeController: REST API를 제공하여 외부에서 커피를 주문할 수 있도록 합니다. @GetMapping을 사용하여 HTTP GET 요청을 처리하고, @PathVariable을 사용하여 주문한 커피 이름을 받습니다.

실행 및 테스트

  • 프로젝트를 실행하고, 브라우저에서 http://localhost:8080/coffee/아메리카노와 같은 URL로 접속하면 "아메리카노 주문 완료!"라는 메시지를 확인할 수 있습니다.

추가 설명

  • @Autowired: 스프링 컨테이너에서 Americano 객체를 찾아 자동으로 주입해주는 역할을 합니다.
  • @Service: CoffeeOrderService를 서비스 빈으로 등록합니다.
  • @RestController: CoffeeController를 REST 컨트롤러로 등록합니다.
  • @GetMapping: HTTP GET 요청을 처리하는 메서드를 표시합니다.
  • @PathVariable: URL 경로에서 변수 값을 추출합니다.
  • @Component: 이 애노테이션은 스프링에게 이 클래스를 빈(Bean)으로 관리하도록 알려줍니다. 즉, 스프링 컨테이너가 이 클래스의 객체를 생성하고 관리하게 됩니다.

 

Long 타입을 사용하는 이유에 대한 쉬운 설명

질문: private Long id; // 학생의 고유 번호, Long 을 쓰는 이유.. long, int 도 있는데 왜 Long을 쓰는지 쉽게 설명해줘

답변:

Java에서 long과 Long은 비슷해 보이지만 중요한 차이점이 있습니다. 둘 다 큰 정수를 저장하기 위한 자료형이지만, 주로 객체 지향 프로그래밍에서 Long을 사용하는 이유는 다음과 같습니다.

1. null 값 허용:

  • Long은 객체이므로 null 값을 가질 수 있습니다. 즉, 아직 값이 할당되지 않았거나 존재하지 않는 값을 나타낼 때 유용합니다.
  • 예를 들어, 데이터베이스에서 특정 학생의 정보를 가져올 때, 해당 학생이 없다면 id를 null로 설정하여 이를 표현할 수 있습니다.
  • 반면, long은 기본형이므로 null 값을 가질 수 없습니다.

2. 컬렉션 및 제네릭:

  • Java의 컬렉션 프레임워크(List, Set, Map 등)는 객체만 저장할 수 있습니다. 따라서 long 값을 컬렉션에 저장하려면 Long으로 박싱(boxing)해야 합니다.
  • 제네릭 타입을 사용할 때도 마찬가지로 Long을 사용해야 합니다.

3. 객체 지향적 설계:

  • Long은 객체이므로 다양한 객체 지향적인 기능(메소드 호출, 상속 등)을 활용할 수 있습니다.
  • 예를 들어, Long 클래스는 parseLong()과 같은 유용한 메소드를 제공합니다.

정리:

  • Long은 null 값을 허용하고, 컬렉션 및 제네릭에서 사용하기 편리하며, 객체 지향적인 설계에 적합합니다.
  • long은 기본형으로 속도가 빠르지만, null 값을 허용하지 않고 객체 지향적인 기능을 사용하기 어렵습니다.

언제 Long을 사용해야 할까요?

  • 데이터베이스 연동: 데이터베이스의 ID 필드는 대부분 null을 허용하므로 Long을 사용하는 것이 일반적입니다.
  • 컬렉션: List, Set, Map 등의 컬렉션에 ID 값을 저장할 때 Long을 사용합니다.
  • 객체 지향 설계: 객체 지향적인 설계를 할 때 Long을 사용하면 더욱 유연하고 확장성 있는 코드를 작성할 수 있습니다.

결론:

User 클래스에서 id를 Long으로 선언한 것은 객체 지향적인 설계 관점에서 바람직한 선택입니다. null 값을 허용하고, 컬렉션 및 제네릭과의 호환성을 고려했을 때 Long이 더 적합합니다.

스프링 부트, MyBatis 기반 자동차 관리 예제 프로그램 작성 가이드

 

일반적으로 스프링 부트를 이용하여 데이터를 CRUD 하는 방식으로 시스템을 구축할 때, 
다음과 같은 순서로 진행하는 것이 일반적입니다.

1)모델(Entity) 정의: 학생 클래스를 통해 데이터베이스 테이블과 매핑될 엔티티를 정의합니다.
2)데이터 접근 계층 (Mapper): Mybatis와 같은 ORM 프레임워크를 이용하여 SQL 쿼리를 작성하고, 자바 객체와 데이터베이스 레코드를 매핑합니다.
3)서비스 계층: 비즈니스 로직을 구현합니다. 데이터베이스에 대한 CRUD 작업을 수행하고, 필요한 경우 트랜잭션을 관리하거나, 다른 서비스를 호출하는 등의 작업을 수행합니다.
4)컨트롤러 계층: HTTP 요청을 처리하고, 서비스 계층을 호출하여 비즈니스 로직을 실행한 후, 결과를 클라이언트에게 반환합니다.

 

 

1. 프로젝트 설정

  • Spring Initializr를 통해 새로운 Spring Boot 프로젝트를 생성합니다.
  • Dependencies: Spring Web, Spring Data JPA, MyBatis, H2 Database를 추가합니다.
  • Build Tool: Maven을 선택합니다.

2. 데이터베이스 설정

  • application.properties: H2 데이터베이스 연결 정보를 설정합니다.

<!-- end list -->

properties spring.datasource.url=jdbc:h2:mem:testdb spring.datasource.driver-class-name=org.h2.Driver spring.datasource.usernam 1 e=sa spring.datasource.password= spring.jpa.hibernate.ddl-auto=create-drop ```  

3. Entity 클래스 생성

  • Car.java: 자동차 정보를 나타내는 엔티티 클래스를 생성합니다.
Java
 
@Data
@Entity
public class Car {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String model;
    private String color;
}

 

4. Mapper 인터페이스 및 XML 생성

  • CarMapper.java: 자동차 관련 SQL 쿼리를 정의하는 Mapper 인터페이스를 생성합니다.

<!-- end list -->

Java
 
public interface CarMapper {
    List<Car> findAll();
    Car findById(Long id);
    int insert(Car car);
    int update(Car car);
    int deleteById(Long id);
}
  • CarMapper.xml: 실제 SQL 쿼리를 작성합니다.

<!-- end list -->

코드 스니펫
 
<?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">
espace="com.example.demo.mapper.CarMapper">
    <select id="findAll" resultType="Car">
        SELECT * FROM CAR
    </select>
    </mapper>

 

5. 서비스 계층 구현

  • CarService.java: CarMapper를 이용하여 비즈니스 로직을 구현합니다.

<!-- end list -->

Java
 
@Service
public class CarService {
    @Autowired
    private CarMapper carMapper;

    public List<Car> getAllCars() {
        return carMapper.findAll();
    }
    // 나머지 메소드 구현
}

 

6. 컨트롤러 계층 구현

  • CarController.java: RESTful API를 제공하는 컨트롤러를 구현합니다.

<!-- end list -->

Java
 
@RestController
@RequestMapping("/cars")
public class CarController {
    @Autowired
    private CarService carService;

    @GetMapping
    public List<Car> getAllCars() {
        return carService.getAllCars();
    }
    // 나머지 API 구현
}

 

7. MyBatis 설정

  • application.properties: MyBatis 설정을 추가합니다.

<!-- end list -->

Properties
 
mybatis.configuration.map-underscore-to-camel-case=true

8. 테스트

  • CarControllerTest.java: JUnit으로 컨트롤러 테스트를 수행합니다.

 

추가 기능 및 고려 사항

  • 검색 기능: 모델명, 색상 등으로 검색하는 기능을 추가할 수 있습니다.
  • 페이징 처리: 많은 데이터를 효율적으로 처리하기 위해 페이징 처리를 구현할 수 있습니다.
  • 예외 처리: 예외 발생 시 적절한 메시지를 반환하도록 처리합니다.
  • 보안: API 보안을 위해 적절한 보안 설정을 적용합니다.
  • 데이터베이스 스키마: 자동차 테이블에 추가적인 필드 (연식, 가격 등)를 추가하여 기능을 확장할 수 있습니다.

주의: 위 예제는 간단한 자동차 관리 시스템을 구축하기 위한 기본적인 구조를 제공합니다. 실제 프로젝트에서는 더 복잡한 비즈니스 로직과 다양한 기능들이 추가될 수 있습니다.

핵심:

  • Entity: 데이터베이스 테이블과 매핑되는 객체
  • Mapper: SQL 쿼리를 정의하는 인터페이스
  • Service: 비즈니스 로직을 구현하는 계층
  • Controller: RESTful API를 제공하는 계층

+ Recent posts