๋ชฉ์ฐจ
์ญ์๋ ๋ฏธ๋์ ๋๋ฅผ ์ํด ๊ธฐ๋กํ๋ค.
๊ณ์ธต๊ตฌ์กฐ ์กฐํ๊ฐ ์ฒ์ ์ ํ๊ณ ์ต์ํด์ง๊ธฐ ์ ๊น์ง๋ ์ข ์ด๋ ต๋ค.(๋ฌผ๋ก ์ง๊ธ๋ ์ฌ์ด ๊ฑด ์๋๋ค.)
๋ชจ๋ ์ผ์ด ์ต์ํด์ง๋ฉด ์๋ฌด๊ฒ๋ ์๋ ๊ฒ ๊ทธ์ ๊น์ง๋ ์ด๋ ค์ด ๋ฒ์ด๋ค.
Rest API ๊ณ์ธต๊ตฌ์กฐ ์กฐํ ๊ฐ๋จ ์์๋ค.
์ฌ์ฉ์(UserDto) ๊ฐ์ฒด๊ฐ ๋์ ๊ฐ์ฒด(BookDto)๋ฅผ list๋ก ๊ฐ์ง๊ณ ์๋ ๊ตฌ์กฐ๋ค.
ํ์๊ณผ ๋์์ ๊ด๊ณ๋ฅผ 1:N์ผ๋ก ๋งคํํ๊ณ ํ์์ด ๋ถ๋ชจ๊ณ ์ฑ ์ด ์์์ด๋ค.
์๋์ฒ๋ผ ํ๋ช ์ ํ์์ด ์ฌ๋ฌ๊ฐ์ ์ฑ ์ ๋์ถํ ์ ์๋ ์กฐ๊ฑด
๊ทธ๋์ ํ์ ๊ฐ์ฒด๋ ๋์ ๊ฐ์ฒด๋ฅผ list๋ก ๊ฐ์ง๊ณ ์์ด์ผ ํ๋ค.
์๋ DB ํ ์ด๋ธ์ ๋ณด๋ฉด ํ์๋ฒํธ 1๋ฒ์ธ ์ฒ๋ฅ์ ์ ํ ๋ฅด๊ฐ ์ดํ๊ฐํธ 1๊ถ~5๊ถ๊น์ง ๊ฐ์ง๊ณ ์๋ค.
API๋๊น ๋น์ฐํ ํด๋ผ์ด์ธํธ์ ์ ๋ฌํ ๊ฐ์ฒด์ธ dto์ db ์กฐ์์ ์ฌ์ฉํ ๊ฐ์ฒด์ธ vo๋ฅผ ๋ฐ๋ก ๋์๋ค.
DB ๐
ํ์ ํ ์ด๋ธ
CREATE TABLE `temp_user` (
`user_seq` int(20) NOT NULL AUTO_INCREMENT COMMENT '์ฌ์ฉ์๋ฒํธ',
`user_name` varchar(30) NOT NULL COMMENT '์ฌ์ฉ์๋ช
',
`user_email` varchar(30) NOT NULL COMMENT '์ฌ์ฉ์ ์ด๋ฉ์ผ',
`reg_date` timestamp NULL DEFAULT NULL COMMENT '๋ฑ๋ก์ผ',
PRIMARY KEY (`user_seq`,`user_email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
ํ ์คํธ ๋ฐ์ดํฐ
INSERT INTO temp_user
(user_seq, user_name, user_email, reg_date)
VALUES(1, 'ํ ๋ฅด', 'thor@naver.com', '2022-12-21 22:57:07.000');
INSERT INTO temp_user
(user_seq, user_name, user_email, reg_date)
VALUES(2, 'ํํฌ', 'hulk@naver.com', '2022-12-21 22:57:07.000');
INSERT INTO temp_user
(user_seq, user_name, user_email, reg_date)
VALUES(3, 'ํ๋
ธ์ค', 'tanos@naver.com', '2022-12-21 22:57:07.000');
๋์ ํ ์ด๋ธ
CREATE TABLE `temp_book` (
`book_seq` int(20) NOT NULL AUTO_INCREMENT COMMENT '๋์ ๋ฒํธ',
`book_name` varchar(30) NOT NULL COMMENT '๋์๋ช
',
`author` varchar(30) NOT NULL COMMENT '์ ์',
`user_seq` int(11) NOT NULL COMMENT '์ฌ์ฉ์๋ฒํธ',
PRIMARY KEY (`book_seq`,`book_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
ํ ์คํธ๋ฐ์ดํฐ
INSERT INTO temp_book
(book_seq, book_name, author, user_seq)
VALUES(25, '์ดํ๊ฐํธ1๊ถ', '์๊ทน์ง', 1);
INSERT INTO temp_book
(book_seq, book_name, author, user_seq)
VALUES(26, '์ดํ๊ฐํธ2๊ถ', '์๊ทน์ง', 1);
INSERT INTO temp_book
(book_seq, book_name, author, user_seq)
VALUES(27, '์ดํ๊ฐํธ3๊ถ', '์๊ทน์ง', 1);
INSERT INTO temp_book
(book_seq, book_name, author, user_seq)
VALUES(28, '์ดํ๊ฐํธ4๊ถ', '์๊ทน์ง', 1);
INSERT INTO temp_book
(book_seq, book_name, author, user_seq)
VALUES(29, '์ดํ๊ฐํธ5๊ถ', '์๊ทน์ง', 1);
INSERT INTO temp_book
(book_seq, book_name, author, user_seq)
VALUES(30, '์ํผ์ค1๊ถ', '์ผ๋ณธ1', 2);
INSERT INTO temp_book
(book_seq, book_name, author, user_seq)
VALUES(31, '์ํผ์ค6๊ถ', '์ผ๋ณธ1', 2);
INSERT INTO temp_book
(book_seq, book_name, author, user_seq)
VALUES(32, '์ํผ์ค7๊ถ', '์ผ๋ณธ1', 2);
INSERT INTO temp_book
(book_seq, book_name, author, user_seq)
VALUES(33, '์ฌ๋จ๋ฉํฌ1๊ถ', '์ผ๋ณธ2', 3);
INSERT INTO temp_book
(book_seq, book_name, author, user_seq)
VALUES(34, '์ฌ๋จ๋ฉํฌ100๊ถ', '์ผ๋ณธ2', 3);
DB ๋์ ํ ์ด๋ธ์ ์ธ๋ํค๋ ํ ์คํธ๋ ๋ ผ๋ฆฌ์ ์ผ๋ก๋ง ์ค์ ํ๋ค.
Java๋จ ๐
Vo(DB์ ํต์ ์ฉ๋)
๋ค์ด๋ฐ ๋ฃฐ์ ์๋ฒ๋จ์ด๋ ์นด๋ฉ์ผ์ด์ค๋ก ์ ์ธ
์๋ ์ธํ ๋ฆฌ์ ์ด์ ํ๋ฌ๊ทธ์ธ์ ์ฌ์ฉํ๋ฉด ์ข ์์ํ๋ค.
[IntelliJ] camelCase <-> snake_case ๋ณํ(feat. CamelCase plugin)
๋ชฉ์ฐจ camelCase snake_case ๐ ๊ฐ๋ฐ์์ ๋์ฒด์ ์ผ๋ก java์์๋ ์นด๋ฉ์ผ์ด์ค(userName)๋ฅผ ์ฌ์ฉํ๊ณ DB ํ๋๋ ์ค์ผ์ดํฌ์ผ์ด์ค(user_name)์ ์ฌ์ฉํ๋๋ฐ ์์์ ์ผ๋ก ํ๋ ๊ฒ๋ณด๋ค ๋ ๊ด์ฐฎ์ ํ๋ฌ๊ทธ์ธ์ ๋ฐ๊ฒฌํด
yaga.tistory.com
package study.lsyrestapitest1.domain.vo;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.ArrayList;
import java.util.List;
@Getter @Setter
@ToString
// ํ์
public class UserVo {
private int userSeq;
private String userName;
private String userEmail;
private String regDate;
private List<BookVo> bookVoList = new ArrayList<>();
}
package study.lsyrestapitest1.domain.vo;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter @Setter @ToString
// ๋์
public class BookVo {
private int bookSeq;
private String bookName;
private String author;
}
Dto(ํด๋ผ์ด์ธํธ์ ํต์ ์ฉ๋)
์ฌ์ด ์์ ๋ฅผ ์ํด ๋น๋ํจํด์ด ์๋ Getter/Setter๋ฅผ ์ด์ฉํ๋ค.
๋ค์ด๋ฐ ๋ฃฐ์ ํด๋ผ์ด์ธํธ๋จ์ด๋ ์ค๋ค์ดํฌ์ผ์ด์ค๋ก ์ ์ธ
package study.lsyrestapitest1.domain.dto;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import java.util.ArrayList;
import java.util.List;
@Getter @Setter
@ToString
@NoArgsConstructor
// ํ์
public class UserDto {
private int user_seq;
private String user_name;
private String user_email;
private String reg_date;
private List<BookDto> book_dto_list = new ArrayList<>();
public UserDto(int user_seq, String user_name, String user_email, String reg_date) {
this.user_seq = user_seq;
this.user_name = user_name;
this.user_email = user_email;
this.reg_date = reg_date;
}
}
package study.lsyrestapitest1.domain.dto;
import lombok.*;
@Getter @Setter
@ToString
@NoArgsConstructor
// ๋์
public class BookDto {
private int book_seq;
private String book_name;
private String author;
public BookDto(int book_seq, String book_name, String author) {
this.book_seq = book_seq;
this.book_name = book_name;
this.author = author;
}
}
mapper xml
resultMap์ collection์ ์ด์ฉํด ๊ณ์ธต๊ตฌ์กฐ๋ฅผ ๋งคํํ๊ณ resultMap์ผ๋ก ๋ฐ์๋ค.
<?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="study.lsyrestapitest1.mapper.UserMapper">
<resultMap id="userMap" type="study.lsyrestapitest1.domain.vo.UserVo">
<id column="user_seq" property="userSeq"/>
<result column="user_name" property="userName"/>
<result column="user_email" property="userEmail"/>
<result column="reg_date" property="regDate"/>
<!-- ์ฑ
list -->
<collection property="bookVoList" javaType="java.util.ArrayList" resultMap="bookMap"/>
</resultMap>
<!-- ์ฑ
-->
<resultMap id="bookMap" type="study.lsyrestapitest1.domain.vo.BookVo">
<id column="book_seq" property="bookSeq"/>
<result column="book_name" property="bookName"/>
<result column="author" property="author"/>
</resultMap>
<select id="findUserList" resultMap="userMap">
SELECT tu.user_seq
,tu.user_name
,tu.user_email
,tu.reg_date
,tb.book_seq
,tb.book_name
,tb.author
FROM temp_user tu LEFT OUTER JOIN temp_book tb ON tu.user_seq = tb.user_seq
</select>
</mapper>
ํน์ ์๋์ฒ๋ผ collection์ ofType์ ๋์ ํด๋์ค๋ก ์ ์ธํ๊ณ resultMap์์๋ค ๋ฃ์ด๋ ๋๋ค.
ofType์ ์ ์ธํ์ง ์์ ๊ฒฝ์ฐ NPE๊ฐ ๋ฐ์ํ๋ค.
<?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="study.lsyrestapitest1.mapper.UserMapper">
<resultMap id="userMap" type="study.lsyrestapitest1.domain.vo.UserVo">
<id column="user_seq" property="userSeq"/>
<result column="user_name" property="userName"/>
<result column="user_email" property="userEmail"/>
<result column="reg_date" property="regDate"/>
<!-- ์ฑ
list -->
<collection property="bookVoList" ofType="study.lsyrestapitest1.domain.vo.BookVo">
<result column="book_seq" property="bookSeq"/>
<result column="book_name" property="bookName"/>
<result column="author" property="author"/>
</collection>
</resultMap>
<select id="findUserList" resultMap="userMap">
SELECT tu.user_seq
,tu.user_name
,tu.user_email
,tu.reg_date
,tb.book_seq
,tb.book_name
,tb.author
FROM temp_user tu LEFT OUTER JOIN temp_book tb ON tu.user_seq = tb.user_seq
</select>
</mapper>
โป hasOne(๋ถ๋ชจ ์์์ ๊ด๊ณ๊ฐ 1:1)๊ด๊ณ๋ association ํ๊ทธ๋ฅผ ์ด์ฉํ๋ฉด ๋๊ณ ์๋์ฒ๋ผ ์ฌ์ฉํ๋ฉด ๋๋ค.
<association property="BookInfo" javaType="com.test.lsy.vo.bookInfo">
<result column="book_no" property="BookNo"/>
<result column="book_nm" property="BookNm"/>
</association>
mapper interface
package study.lsyrestapitest1.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import study.lsyrestapitest1.domain.vo.UserVo;
import java.util.List;
@Mapper @Repository
public interface UserMapper {
List<UserVo> findUserList();
}
service
DB ์กฐํ ๊ฐ์ Voํ์ list์ ๋ฐ์ list๋ฅผ ๋ฃจํ ๋๋ฉฐ ์๋ก ๋ง๋ dto๊ฐ์ฒด์ ์ธํ ์ ์ํจ ํ DtoList ๋ฐํ
๋ฌผ๋ก stream ๋ฑ์ ์ด์ฉํด ๋ ์ฝ๋๋ฅผ ๊ฐ๊ฒฐํ ํ ์๋ ์์ง๋ง ๋๊ตฌ๋ ๋ณด๊ธฐ ์ฝ๊ฒ ํฅ์๋ for๋ฌธ ์ด์ฉ
์๋๋ฉด ๊ทธ๋ฅ mapper์์๋ถํฐ dto๋ก ๋ฐ์ผ๋ฉด ์๋น์ค ์ฝ๋๋ ๋งค์ฐ ๊ฐ๊ฒฐํด ์ง๋ค.(์ด๊ฒ ๋ ๋์ ๋ฏ ํ๋ค.)
package study.lsyrestapitest1.service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import study.lsyrestapitest1.domain.dto.BookDto;
import study.lsyrestapitest1.domain.dto.UserDto;
import study.lsyrestapitest1.domain.vo.BookVo;
import study.lsyrestapitest1.domain.vo.UserVo;
import study.lsyrestapitest1.mapper.UserMapper;
import java.util.ArrayList;
import java.util.List;
@Service
@Slf4j
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class UserService {
private final UserMapper userMapper;
public List<UserDto> findUserList() {
// DB๊ฐ ์กฐํํ ๊ฐ์ userVoList์ ์ ์ฅ
List<UserVo> userVoList = userMapper.findUserList();
List<UserDto> userDtoList = new ArrayList<>();
// userVoList๋ฅผ ๋ฃจํ ๋๋ฉฐ UserDto๊ฐ์ฒด๋ฅผ ์์ฑ ํด Vo์ ๊ฐ์ dto์ ์ธํ
for (UserVo userVo : userVoList) {
UserDto userDto = new UserDto(userVo.getUserSeq(), userVo.getUserName(), userVo.getUserEmail(), userVo.getRegDate());
// UserVo์ bookVoList๊ฐ์ bookVoList์ ์ ์ฅ ํ ๋ฃจํ ๋๋ฉฐ bookDtoList์ ์ ์ฅ
List<BookVo> bookVoList = userVo.getBookVoList();
List<BookDto> bookDtoList = new ArrayList<>();
for (BookVo bookVo : bookVoList) {
BookDto bookDto = new BookDto(bookVo.getBookSeq(), bookVo.getBookName(), bookVo.getAuthor());
bookDtoList.add(bookDto);
// userDto์ book_dto_list์ bookVoList์ ๊ฐ์ ์ธํ
userDto.setBook_dto_list(bookDtoList);
}
// userDtoList์ userDto ๊ฐ์ฒด ์ถ๊ฐ
userDtoList.add(userDto);
}
return userDtoList;
}
}
controller
๋ฐํ data๋ฅผ view ์์ด Http Body์ ์ค์ด HttpStatus 200์ฝ๋์ ํจ๊ป ๋ฐํํ๋ค.
@RestController๋ @Controller์ @ResponseBody๊ฐ ํฉ์ณ์ง ์ ๋ ธํ ์ด์ ์ด๋ค.
package study.lsyrestapitest1.controller.api;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import study.lsyrestapitest1.domain.dto.UserDto;
import study.lsyrestapitest1.domain.vo.UserVo;
import study.lsyrestapitest1.service.UserService;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@Slf4j
@RequiredArgsConstructor
@RequestMapping(value = "/api/v1/users")
public class UserApiController {
private final UserService userService;
@GetMapping(value = "")
public ResponseEntity<?> findUserList() {
Map<String, Object> resultMap = new HashMap<>();
try {
resultMap.put("resultData", userService.findUserList());
resultMap.put("resultCode", "0000");
resultMap.put("resultMsg", "์ ์์ ์ผ๋ก ์ฒ๋ฆฌ๋์์ต๋๋ค.");
return new ResponseEntity(resultMap, HttpStatus.OK);
} catch (Exception e) {
log.info("exception :: {}", e.getMessage());
resultMap.put("resultCode", "9999");
resultMap.put("errorMsg", e.getMessage());
resultMap.put("resultMsg", "๋ด๋ถ ์๋น์ค ์ค๋ฅ์
๋๋ค.");
return new ResponseEntity(resultMap, HttpStatus.BAD_REQUEST);
}
}
}
ํ ์คํธ
๋ด๊ฐ ์ํ ๋ฐ์ดํฐ ๊ตฌ์กฐ๋๋ก ์ ๋ฐํ์ด ๋๋ค.
Map({})์์ list([])์์ uerDto๊ฐ์ฒด({book_dto_list[]})๊ฐ ๋ด๊ฒจ ์๊ณ http ์ํ์ฝ๋ 200์ด ๋ฐํ๋์๋ค.
์ฒ์์๋ ๊ฐ์ฒด์์ ๊ฐ์ฒดํ์ ๋ฆฌ์คํธ๊ฐ ์๋ ๊ตฌ์กฐ๊ฐ ๋ฌด์ฒ ํท๊ฐ๋ ธ๋๋ฐ ์ต์ํด์ง๋ ์ข ๋์์ก๋ค.
์ค๋ฌด๋ ๋ ๋ณต์กํ ๊ตฌ์กฐ๊ฐ ๋ง๊ธฐ์ ์ด ๊ฐ๋จํ ๊ตฌ์กฐ๋ถํฐ ์ดํด๊ฐ ๋์ด์ผ ์์ฉ์ด ๊ฐ๋ฅํ๋ค.
ํ๋ก์ ํธ ์์ฑ ํ ํจํค์ง๋ช ๋ง ๋ณ๊ฒฝํด์ ๊ตฌํํ๋ฉด ๊ฒฐ๊ณผ๊ฐ์ด ์ ๋์ฌ ๊ฒ์ด๋ค.
์ด๋ ๊ฒ mybatis์์ ๊ณ์ธต ๋งคํํ๋ ๋ฐฉ๋ฒ๋ ์๊ณ ์ฟผ๋ฆฌ๋ก ๋ฐ์ดํฐ ํ๋ฒ์ ๊ฐ์ ธ์์ list์ ๋ด์ java๋จ์์ for๋ฌธ ๋๋ ค์ ๋งคํํ๋ ๋ฐฉ๋ฒ์ด ์๋ค.

'IT > development' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[devTool] postman์ผ๋ก ํ์ผ ์ ์ก ํ ์คํธ (0) | 2023.01.30 |
---|---|
[spring] @RequestBody String type ๋ฐ๊ธฐ (0) | 2023.01.24 |
[springBoot/html] table๊ฐ ๋ณ๊ฒฝ ํ DB update(feat.contenteditable) (0) | 2022.12.17 |
[ํฐ์คํ ๋ฆฌ] ์ฝ๋๋ธ๋ญ ํด๋ฆฝ๋ณด๋์ ๋ณต์ฌ ์ถ๊ฐํ๊ธฐ(feat. clipboard.js) (0) | 2022.12.17 |
[mybatis] mybatis multi update(๋ค์ค ์ ๋ฐ์ดํธ) (0) | 2022.12.16 |
๋๊ธ