1108 - 1114
# 1108 - 1114
# 1108 - ssl
# SSL์ด๋?
SSL(Secure Socket Layer)์ ์ํธํ ๊ธฐ๋ฐ ์ธํฐ๋ท ๋ณด์ ํ๋กํ ์ฝ์ด๋ค. ์ธํฐ๋ท ํต์ ์ ๊ฐ์ธ์ ๋ณด ๋ณดํธ, ์ธ์ฆ, ๋ฐ์ดํฐ ๋ฌด๊ฒฐ์ฑ์ ๋ณด์ฅํ๊ธฐ ์ํด Netscape๊ฐ 1995๋ ์ฒ์์ผ๋ก ๊ฐ๋ฐํ๋ค. SSL์ ํ์ฌ ์ฌ์ฉ ์ค์ธ TLS ์ํธํ์ ์ ์ ์ด๋ค.
SSL/TLS๋ฅผ ์ฌ์ฉํ๋ ์น์ฌ์ดํธ์ URL์๋ "HTTP"๋์ "HTTPS"๊ฐ ์๋ค.
# SSL/TLS ์๋์๋ฆฌ
- SSL์ ๋์ ์์ค์ ๊ฐ์ธ์ ๋ณด ๋ณดํธ๋ฅผ ์ ๊ณตํ๊ธฐ ์ํด, ์น์์ ์ ์ก๋๋ ๋ฐ์ดํฐ๋ฅผ ์ํธํํ๋ค. ๋ฐ๋ผ์, ๋ฐ์ดํฐ๋ฅผ ๊ฐ๋ก์ฑ๋ ค๋ ์๋ ๊ฑฐ์ ํด๋ ํ ์ ์๋ ๋ณต์กํ ๋ฌธ์๋ง ๋ณด๊ฒ ๋๋ค.
- SSL์ ๋ ํต์ ์ฅ์น ์ฌ์ด์ ํธ๋์ ฐ์ดํฌ๋ผ๋ ์ธ์ฆ ํ๋ก์ธ์ค๋ฅผ ์์ํ์ฌ ๋ ์ฅ์น์ ID๋ฅผ ํ์ธํ๋ค.
- SSL์ ๋ํ ๋ฐ์ดํฐ ๋ฌด๊ฒฐ์ฑ์ ์ ๊ณตํ๊ธฐ ์ํด ๋ฐ์ดํฐ์ ๋์งํธ ์๋ช ํ์ฌ ๋ฐ์ดํฐ๊ฐ ์๋๋ ์์ ์์ ๋์ฐฉํ๊ธฐ ์ ์ ์กฐ์๋์ง ์์๋ค๋ ๊ฒ์ ํ์ธํ๋ค.
SSL์ ์ฌ๋ฌ ๋ฒ ๊ฐ์ ๋์ด ๋งค๋ฒ ์ฑ๋ฅ์ด ๊ฐ์ ๋์์ผ๋ฉฐ, 1999๋ ์ SSL์ TLS๋ก ์ ๋ฐ์ดํธ ๋๋ค.
# SSL/TLS์ ์ค์์ฑ
- ์, ์ฃผ๋ฌธ ๊ณผ์ ์์ ๊ณ ๊ฐ์ ์ ์ฉ ์นด๋ ๋ฒํธ๊ฐ ์ฌ์ฉ๋์๋ ํ์ทจ์์๊ฒ ๋ ธ์ถ๋๊ฒ ๋๋ค.
- ๋ํ SSL์ ํน์ ํ ์ ํ์ ์ฌ์ด๋ฒ ๊ณต๊ฒฉ๋ ์ฐจ๋จํ๋ค.
# SSL/TLS์ ์ฐจ์ด
- SSL์ TLS(Transport Layer Security)๋ผ๋ ๋ ๋ค๋ฅธ ํ๋กํ ์ฝ์ ๋ฐ๋ก ์ด์ ๋ฒ์ ์ด๋ค. 1999๋ IETF๋ SSL์ ๋ํ ์ ๋ฐ์ดํธ๋ฅผ ์ ์ํ๊ณ , Netscape๊ฐ ๋ ์ด์ ์ฐธ์ฌํ์ง ์๊ฒ ๋๋ฉด์, ์ด๋ฆ์ด TLS๋ก ๋ณ๊ฒฝ๋์๋ค.
# SSL์ธ์ฆ์ด๋?
SSL์ SSL ์ธ์ฆ์๊ฐ ์๋ ์น์ฌ์ดํธ๋ง ์คํํ ์ ์๋ค. SSL ์ธ์ฆ์๋ ์น์ฌ์ดํธ๋ ์ ํ๋ฆฌ์ผ์ด์ ์๋ฒ๊ฐ ์น์ ์ ์ฅํ๊ณ ํ์ํ๋ค.
SSL ์ธ์ฆ์์ ํฌํจ๋ ๊ฐ์ฅ ์ค์ํ ์ ๋ณด์ ์น์ฌ์ดํธ์ ๊ณต๊ฐ ํค๊ฐ ์๋ค. ์ด ๊ณต๊ฐ ํค ๋๋ถ์ ์ํธํ๊ฐ ๊ฐ๋ฅํ๋ฉฐ, ์ฌ์ฉ์์ ์ฅ์น๋ ๊ณต๊ฐ ํค๋ฅผ ๋ณด๊ณ ์ด๋ฅผ ์ด์ฉํ์ฌ ์น ์๋ฒ์ ์์ ํ ์ํธํ ํค๋ฅผ ์๋ฆฝํ๋ค. ํํธ, ์น ์๋ฒ์๋ ๊ธฐ๋ฐ๋ก ์ ์งํ๋ ๊ฐ์ธ ํค๊ฐ ์๋ค. ๊ฐ์ธ ํค๋ ๊ณต๊ฐ ํค๋ก ์ํธํ๋ ๋ฐ์ดํฐ๋ฅผ ํด๋ ํ๋ค. CA(์ธ์ฆ ๊ธฐ๊ด)๋ SSL ์ธ์ฆ์ ๋ฐํ์ ๋ด๋นํ๋ค.
# SSL ์ธ์ฆ์ ์ ํ
- ๋จ์ผ ๋๋ฉ์ธ
- ์์ผ๋์นด๋
- ๋ฉํฐ ๋๋ฉ์ธ
- ๋๋ฉ์ธ ์ ํจ์ฑ ๊ฒ์ฌ
- ์กฐ์ง ์ ํจ์ฑ ๊ฒ์ฌ
- ํ์ฅ ์ ํจ์ฑ ๊ฒ์ฌ
# 1110 - MapStruct(๋งต์คํธ๋ญํธ)
Model mapping์ ์์ฃผ ์ฝ๊ฒ ํด์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ.
# ๋งต์คํธ๋ญํธ ์ฌ์ฉ๋ฒ
# build.gradle
buildscript {
ext {
mapstructVersion = '1.3.1.Final'
}
}
...
// Mapstruct
implementation "org.mapstruct:mapstruct:${mapstructVersion}"
annotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}"
testAnnotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}"
...
compileJava {
options.compilerArgs = [
'-Amapstruct.suppressGeneratorTimestamp=true',
'-Amapstruct.suppressGeneratorVersionInfoComment=true'
]
}
# java
@Data
@AllArgsConstructor(staticName = "of")
@NoArgsConstructor
public class CarDto {
private String name;
private String color;
}
---
@Data
@AllArgsConstructor(staticName = "of")
@NoArgsConstructor
public class Car {
private String modelName;
private String modelColor;
}
---
@Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
@Mapping(source = "name", target = "modelName")
@Mapping(source = "color", target = "modelColor")
Car to(CarDto carDto);
}
๊ฐ๊ฐ dto, domain, mapper
public class MapstructTest {
@Test
public void test() {
CarDto carDto = CarDto.of("bmw x4", "black");
Car car = CarMapper.INSTANCE.to(carDto);
assertEquals(carDto.getName(), car.getModelName());
assertEquals(carDto.getColor(), car.getModelColor());
}
}
๊ฐ๋จํ ํ ์คํธ ์ฝ๋
- ์ค์ ์์ฑ๋ ์ฝ๋
@Generated(
value = "org.mapstruct.ap.MappingProcessor"
)
public class CarMapperImpl implements CarMapper {
@Override
public Car to(CarDto carDto) {
if ( carDto == null ) {
return null;
}
Car car = new Car();
car.setModelName( carDto.getName() );
car.setModelColor( carDto.getColor() );
return car;
}
}
# ํ๋์ ๊ฐ์ฒด๋ก ํฉ์น๊ธฐ
- ์ฌ๋ฌ ๊ฐ์ฒด์ ํ๋๊ฐ์ ํ๋์ ๊ฐ์ฒด๋ก ํฉ์น๊ธฐ๊ฐ ๊ฐ๋ฅํ๋ค. ํน๋ณํ ๋ค๋ฅธ ์ต์ ์ ๋ฃ๋ ๊ฒ์ ์๋๋ค.
@Data
@AllArgsConstructor(staticName = "of")
@NoArgsConstructor
public class UserDto {
private String name;
}
---
@Data
@AllArgsConstructor(staticName = "of")
@NoArgsConstructor
public class AddressDto {
private String si;
private String dong;
}
---
@Mapper
public interface UserInfoMapper {
UserInfoMapper INSTANCE = Mappers.getMapper(UserInfoMapper.class);
@Mapping(source = "user.name", target = "userName")
@Mapping(source = "address.si", target = "si")
@Mapping(source = "address.dong", target = "dong")
UserInfo to(UserDto user, AddressDto address);
}
---
@Generated(
value = "org.mapstruct.ap.MappingProcessor"
)
public class UserInfoMapperImpl implements UserInfoMapper {
@Override
public UserInfo to(UserDto user, AddressDto address) {
if ( user == null && address == null ) {
return null;
}
UserInfo userInfo = new UserInfo();
if ( user != null ) {
userInfo.setUserName( user.getName() );
}
if ( address != null ) {
userInfo.setDong( address.getDong() );
userInfo.setSi( address.getSi() );
}
return userInfo;
}
}
# ์ด๋ฏธ ์์ฑ๋ ๊ฐ์ฒด์ ๋งคํ
- ์๋ก์ด ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ ๊ฒ์ด ์๋๋ผ, ๊ธฐ์กด์ ์ด๋ฏธ ์์ฑ๋์ด ์๋ ๊ฐ์ฒด์ ๋งคํ์ด ํ์ํ ๊ฒฝ์ฐ
@Mapper
public interface UserInfoMapper {
UserInfoMapper INSTANCE = Mappers.getMapper(UserInfoMapper.class);
@Mapping(source = "user.name", target = "userName")
@Mapping(source = "address.si", target = "si")
@Mapping(source = "address.dong", target = "dong")
void write(UserDto user, AddressDto address, @MappingTarget UserInfo userInfo);
}
@Generated(
value = "org.mapstruct.ap.MappingProcessor"
)
public class UserInfoMapperImpl implements UserInfoMapper {
@Override
public void write(UserDto user, AddressDto address, UserInfo userInfo) {
if ( user == null && address == null ) {
return;
}
if ( user != null ) {
userInfo.setUserName( user.getName() );
}
if ( address != null ) {
userInfo.setDong( address.getDong() );
userInfo.setSi( address.getSi() );
}
}
}
# ํ์ ๋ณํ
- ๋๋ถ๋ถ์ ์์์ ์ธ ์๋ ํ๋ณํ์ด ๊ฐ๋ฅํ๋ค. (Integer -> String ...) ๋ค์์ ์กฐ๊ธ ์ ์ฉํ ๊ธฐ๋ฅ์ด๋ค.
@Mapper
public interface CarMapper {
@Mapping(source = "price", numberFormat = "$#.00")
CarDto carToCarDto(Car car);
@IterableMapping(numberFormat = "$#.00")
List<String> prices(List<Integer> prices);
}
# Source, Target mapping policy
- ๋งคํ๋ ํ๋๊ฐ ์กด์ฌํ์ง ์์ ๋, ์๊ฒฉํ ์ ์ฑ ์ ๊ฐ์ ธ๊ฐ๊ธฐ ์ํ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
@Data
@AllArgsConstructor(staticName = "of")
@NoArgsConstructor
public class Car {
private String modelName;
private String modelColor;
private String modelPrice;
private String description;
}
@Data
@AllArgsConstructor(staticName = "of")
@NoArgsConstructor
public class CarDto {
private String name;
private String color;
private Integer price;
}
@Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR)
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
@Mapping(source = "name", target = "modelName")
@Mapping(source = "color", target = "modelColor")
@Mapping(source = "price", target = "modelPrice", numberFormat = "$#.00")
Car to(CarDto carDto);
}
์ ์ฝ๋๋ ํ๊ฒ์ด ๋๋ ์ค๋ธ์ ํธ ํ๋์ ๋ํ ์ ์ฑ ์ ๊ฐ์ ธ๊ฐ๋ค. Car ํด๋์ค์๋ description ํ๋๊ฐ ์๋๋ฐ, CarDto ํด๋์ค์๋ ํด๋น ํ๋๊ฐ ์กด์ฌํ์ง ์๊ธฐ ๋๋ฌธ์ ์ปดํ์ผ์ ์ปดํ์ผ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.(ERROR, IGNORE, WARN ์ ์ฑ ์กด์ฌ) ๋ง์ฝ ํน์ ํ๋๋ ํด๋น ์ ์ฑ ์ ํผํ๊ณ ์ถ๋ค๋ฉด ์๋์ ๊ฐ์ด ์ด๋ ธํ ์ด์ ํ๋๋ฅผ ๋ฌ์์ค๋ค.
@Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR)
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
@Mapping(source = "name", target = "modelName")
@Mapping(source = "color", target = "modelColor")
@Mapping(source = "price", target = "modelPrice", numberFormat = "$#.00")
@Mapping(target = "description", ignore = true)
Car to(CarDto carDto);
}
# null ์ ์ฑ
- Source๊ฐ null์ด๊ฑฐ๋ ํน์ Source์ ํน์ ํ๋๊ฐ null์ผ๋ ์ ์ฉ๊ฐ๋ฅํ ์ ์ฑ ์ด ์กด์ฌํ๋ค.
@Mapper(
unmappedTargetPolicy = ReportingPolicy.ERROR,
nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT
)
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
@Mapping(source = "name", target = "modelName")
@Mapping(source = "color", target = "modelColor")
@Mapping(source = "price", target = "modelPrice", numberFormat = "$#.00")
@Mapping(target = "description", ignore = true)
Car to(CarDto carDto);
}
์ ์ฝ๋๋ Source ์ค๋ธ์ ํธ๊ฐ null์ผ๋, ๊ธฐ๋ณธ์์ฑ์๋ก ํ๋๊ฐ ๋น์ด์๋ Target ์ค๋ธ์ ํธ๋ฅผ ๋ฐํํด์ค๋ค.
@Mapper(
unmappedTargetPolicy = ReportingPolicy.ERROR,
nullValueMappingStrategy = NullValueMappingStrategy.RETURN_NULL
)
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
@Mapping(source = "name", target = "modelName")
@Mapping(source = "color", target = "modelColor")
@Mapping(source = "price", target = "modelPrice", numberFormat = "$#.00")
@Mapping(
source = "description",
target = "description",
ignore = true,
nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT
)
Car to(CarDto carDto);
}
์ ์ฝ๋๋ ๊ฐ ํ๋์ ๋ํด null์ ์ฑ ์ ๋ถ์ฌํ๋ค. ๋ง์ฝ SET_TO_DEFAULT๋ก ์ค์ ํ๋ฉด, List ์ผ๋๋ ๋น ArrayList๋ฅผ ์์ฑํด์ฃผ๊ณ , String์ ๋น๋ฌธ์์ด, ํน์ ์ค๋ธ์ ํธ๋ผ๋ฉด ํด๋น ์ค๋ธ์ ํธ์ ๊ธฐ๋ณธ ์์ฑ์ ๋ฑ์ผ๋ก ๊ธฐ๋ณธ๊ฐ์ ์์ฑํด์ค๋ค.
# ํน์ ํ๋ ๋งคํ ๋ฌด์
- ํน์ ํ๋๋ ๋งคํ๋์ง ์๊ธธ ์ํ๋ค๋ฉด @Mapping ์ด๋ ธํ ์ด์ ์ ignore = true ์์ฑ์ ๋ฃ์ด์ค๋ค.
@Mapper(
unmappedTargetPolicy = ReportingPolicy.ERROR,
nullValueMappingStrategy = NullValueMappingStrategy.RETURN_NULL
)
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper(CarMapper.class);
@Mapping(source = "name", target = "modelName")
@Mapping(source = "color", target = "modelColor")
@Mapping(source = "price", target = "modelPrice", numberFormat = "$#.00")
@Mapping(target = "description", ignore = true)
Car to(CarDto carDto);
}
public class MapstructTest {
@Test
public void test() {
CarDto carDto = CarDto.of(
"bmw x4",
"black",
10000,
"description");
Car car = CarMapper.INSTANCE.to(carDto);
System.out.println(car.toString());
}
}
result =>
Car(modelName=bmw x4, modelColor=black, modelPrice=$10000.00, description=null)
# ๋งคํ ์ ์ฒ๋ฆฌ, ํ์ฒ๋ฆฌ
- ๋งคํํ๊ธฐ ์ด์ ๊ณผ ๋งคํ ์ดํ ํน์ ๋ก์ง์ ์ฃผ์ ์ํฌ ์ ์๋ค.
@Mapper(
unmappedTargetPolicy = ReportingPolicy.ERROR,
nullValueMappingStrategy = NullValueMappingStrategy.RETURN_NULL,
componentModel = "spring"
)
public abstract class CarMapper {
@BeforeMapping
protected void setColor(CarDto carDto, @MappingTarget Car car) {
if (carDto.getName().equals("bmw x4")) {
car.setModelColor("red");
} else {
car.setModelColor("black");
}
}
@Mapping(source = "name", target = "modelName")
@Mapping(target = "modelColor", ignore = true)
@Mapping(source = "price", target = "modelPrice", numberFormat = "$#.00")
public abstract Car to(CarDto carDto);
@AfterMapping
protected void setDescription(@MappingTarget Car car) {
car.setDescription(car.getModelName() + " " + car.getModelColor());
}
}
<Generate Code>
@Generated(
value = "org.mapstruct.ap.MappingProcessor"
)
@Component
public class CarMapperImpl extends CarMapper {
@Override
public Car to(CarDto carDto) {
if ( carDto == null ) {
return null;
}
Car car = new Car();
setColor( carDto, car );
car.setModelName( carDto.getName() );
if ( carDto.getPrice() != null ) {
car.setModelPrice( new DecimalFormat( "$#.00" ).format( carDto.getPrice() ) );
}
car.setDescription( carDto.getDescription() );
setDescription( car );
return car;
}
}
์ ์ฒ๋ฆฌ์ ํ์ฒ๋ฆฌ๋ฅผ ์ํ ๋ฉ์๋๋ private๋ฅผ ์ฌ์ฉํด์๋ ์๋๋ค. ๊ทธ ์ด์ ๋ generate๋ ์ฝ๋์ ์ , ํ ์ฒ๋ฆฌ ๋ฉ์๋๊ฐ ๋ค์ด๊ฐ๋ ๊ฒ์ด ์๋๋ผ ์ถ์ ํด๋์ค์ ์๋ ๋ฉ์๋๋ฅผ ๊ทธ๋๋ก ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ด๋ค.
- ๊ณต์ ๋ ํผ๋ฐ์ค https://mapstruct.org/documentation/stable/reference/html/
# 1112 - custom exception ์ฅ๋จ์
# ํ์ค ์์ธ ์ฌ์ฉ์ ์ฅ์
- ์์ธ ๋ฉ์์ง๋ก๋ ์ถฉ๋ถํ ์๋ฏธ๋ฅผ ์ ๋ฌํ ์ ์๋ค.
- ์ ํจํ์ง ์์ ์ ๋ ฅ๊ฐ์ ๋ํ ์์ธ ๊ฐ์ ๊ฒฝ์ฐ ์๋ฐ์์ ์ ์ํด ๋์ IllegalArgumentException์ ์ฌ์ฉํ๊ณ ๋ฉ์์ง๋ง ์์ธ์ฌํญ์ ๋ง๊ฒ ์ฌ์ ์ํด์ค๋ค๋ฉด ์ถฉ๋ถํ ๊ทธ ์๋ฏธ๋ฅผ ํ์ ํ ์ ์๋ค.
- ํ์ค ์์ธ๋ฅผ ์ฌ์ฉํ๋ฉด ๊ฐ๋ ์ฑ์ด ๋์์ง๋ค.
- ์ธ์๋ก ๋ถ์ ์ ํ ๊ฐ์ด ๋ค์ด์ฌ ๋ ๋์ง๋ ์์ธ์ธ IllegalArgumentException, ์ผ์ ์ํํ๊ธฐ์ ์ ํฉํ์ง ์์ ์ํ์ ๊ฐ์ฒด์ธ ๊ฒฝ์ฐ ๋์ง๋ ์์ธ์ธ IllegalStateException, ์์ฒญ๋ฐ์ ์์
์ ์ง์ํ์ง ์๋ ๊ฒฝ์ฐ์ ๋์ง๋ ์์ธ์ธ UnsupportedOperationException ๋ฑ, ์ฐ๋ฆฌ๋ ์ด๋ฏธ ์ต์ํ๊ณ , ์ฐ์์ ๋ํด ์ ์๊ณ ์๋ ์์ธ๋ค์ด ๋ง๋ค.
์ด๋ฐ ์์ธ๋ค์ด ์๋ ์ฒ์ ๋ณด๋ ์์ธ๋ค์ ๋น์ฐํ ๊ตฌ์ฒด์ ์ธ ์ฐ์์ ์ ๋ชจ๋ฅธ๋ค. ์ด๋ฐ ์ด์ ๋ก ๋ฏ์ ์์ธ๋ณด๋ค๋ ์ต์ํ ์์ธ๋ฅผ ๋ง์ฃผ์น๋ ๊ฒ์ด ๋น์ฐํ ๊ฐ๋์ฑ์ด ๋์ ์ ๋ฐ์ ์๋ค.
๋ํ, ๋ฏ์ ์์ธ๋ฅผ ๋ง๋ฌ์ ๋, ๋น์ฐํ๊ฒ๋ ๊ทธ ์ปค์คํ ์ต์ ์ ์ ํ์ ํ๋ ์์ ์ด ๋ฐ๋ผ์จ๋ค. ์ด ๋ํ ๋น์ฉ์ด ๋ ์ ์๋ค.
ํ์ค ์์ธ์ ๋ํ ์ฐ์์ ๊ณต์๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ๋ฉด ๋๋ค. https://docs.oracle.com/javase/8/docs/api/?java/lang/RuntimeException.html
- ์ผ์ผํ ์์ธ ํด๋์ค๋ฅผ ๋ง๋ค๋ค๋ณด๋ฉด ์ง๋์น๊ฒ ์ปค์คํ ์์ธ๊ฐ ๋ง์์ง ์ ์๋ค.
- ๋ง์ domain์ด ์๊ธฐ๊ณ ๋๋ฉ์ธ ๋ณ ์ ์๋ custom Exception๋ค์ด ์๊ธธ ๊ฒฝ์ฐ ์ง๋์น๊ฒ ๋ง์์ง ์ ์๋ค. ์ด ๋๋ ํ ๋ฆฌ์ ํด๋์ค๋ฅผ ๊ด๋ฆฌํ๋ ๊ฒ ์ญ์ ์ผ์ด๋ฉฐ, ์ง๋์น๊ฒ ๋ง์์ง๋ค๋ฉด ๋ฉ๋ชจ๋ฆฌ ๋ฌธ์ ๋ ๋ฐ์ํ ์ ์๊ณ , ํด๋์ค ๋ก๋ฉ์๋ ์๊ฐ์ด ๋ ์์๋ ๊ฐ๋ฅ์ฑ์ด ์๋ค.
# ์ปค์คํ ์์ธ ์ฌ์ฉ์ ์ฅ์
์ฌ์ฉ์ ์ ์ ์์ธ๊ฐ ํ์ํ๋ค.
- Spring์ @Valid ์ด๋ ธํ ์ด์ ์ ์์ธ ์ํฉ์์ MethodArgumentNotValidException์ ๋ฐ์์ํจ๋ค. ํด๋น ์์ธ์ ํจํค์ง ๊ตฌ์กฐ๋ org.springframework.web.bind.MethodArgumentNotValidException ์ผ๋ก Spring์์ ๋ง๋ ์ฌ์ฉ์ ์ ์ ์์ธ๋ค.
- ์ด๋ฆ์ผ๋ก๋ ์ ๋ณด ์ ๋ฌ์ด ๊ฐ๋ฅํ๋ค.
- NoSuchElementException ๋ง์ผ๋ก๋ ์ด๋ค ์์๊ฐ ์๋์ง ์ ์ ์๋ค. ํ์ง๋ง, PostNotFoundException์ด ๋ฐ์ํ๋ฐ๋ฉด, Post๋ฅผ ์ฐพ๋ ์์ฒญ์ ๋ณด๋์ง๋ง ํด๋น ์์๊ฐ ์๋ค๋ ์ํฉ์ ์ ์ถํ ์ ์์ ๊ฒ์ด๋ค.
์ด์ฒ๋ผ Custom Exception์ ์ด๋ฆ์ ํตํด ์ผ์ฐจ์ ์ผ๋ก ์์ธ ๋ฐ์ ์ํฉ์ ๋ํด ์ ์ถํ ์ ์๋ ์ ๋ณด๋ฅผ ์ ๊ณตํ๋ค.
- ์์ธํ ์์ธ ์ ๋ณด๋ฅผ ์ ๊ณตํ ์ ์๋ค.
- ์ปฌ๋ ์ ์ ๋ฒ์๋ฅผ ๋ฒ์ด๋ index ์ ๊ทผ ์์ฒญ์ด ์๊ฒผ๋ค๊ณ ๊ฐ์ ํ์๋, ๊ธฐ์กด์ ์์ธ์์ IllegalArgumentException์ด๋ IndexOutOfBoundsException์ ํ๋ณด๋ก ์๊ฐํด๋ณผ ์ ์์ ๊ฒ์ด๋ค. ์์ธ ๋ฉ์์ง๋ก๋ "๋ฒ์๋ฅผ ๋ฒ์ด๋ฌ์ต๋๋ค." ์ ๋๋ฉด ์ ๋นํ๋ค.
if (index >= arr.length) {
throw new IndexOutOfBoundsException("๋ฒ์๋ฅผ ๋ฒ์ด๋ฌ์ต๋๋ค.");
}
- ํ์ง๋ง ์ ์ฒด ๋ฒ์๊ฐ ์ผ๋ง์ธ์ง, ์์ฒญํ index๊ฐ ๋ช์ธ์ง ํ์
ํ๊ธฐ ์ฐํด์๋ ํ๋ก๊ทธ๋๋จธ๊ฐ ์ง์ ๋๋ฒ๊น
ํ๊ฑฐ๋ ์ ๋ณด๋ฅผ ๋ด์ ๋ฉ์์ง๋ฅผ ๋ง๋ค์ด์ค์ผ ํ๋ค. ๋๋ฒ๊ทธ๋ฅด๋ฅ ํตํด ์ง์ ์ ๋ณด๋ฅผ ์ฐพ์๋ด๋ ํ์๋ ์ฉ ์ข์ ์ผ์ด ์๋๋ค. ๊ฐ๋ฐ ๊ณผ์ ์ ํผ๋กํจ๋ง ๋์ด๊ฐ๋ค. ๋ฉ์์ง๋ ๋จ ํ ๊ณณ์์๋ง ๋ฐ์ํ๋ ์์ธ๋ผ๋ฉด ์๊ด์์ง๋ง ์ฌ๋ฌ ๊ณณ์์ ๋ฐ์ํ๋ค๋ฉด ๋ฆฌํฉํ ๋งํ๊ธฐ๊ฐ ํ๋ค์ด ์ง ๊ฒ์ด๋ค.
์ด๋ฌํ ์ํฉ์์ ์ฌ์ฉ์ ์ ์ ์์ธ๋ ์ข์ ํด๊ฒฐ์ฑ ์ด ๋ ์ ์๋ค.
public class IllegalIndexException extends IndexOutOfBoundsException {
private static final String message = "๋ฒ์๋ฅผ ๋ฒ์ด๋ฌ์ต๋๋ค.";
public IllegalIndexException(List<?> target, int index) {
super(message + " size: " + target.size() + " index: " + index);
}
}
- ์๋ฅผ ๋ณด๋ฉด ์์ฒญ ๋ฐ์ ์ปฌ๋ ์
์ ์ต๋ ๋ฒ์๊ฐ ์ด๋๊น์ง์ธ์ง, ์์ฒญํ index๋ ๋ช์ธ์ง ํ์ธ ๊ฐ๋ฅํ๋ค. try/catch๋ฅผ ํตํด ๋ฐ์ํ ์์ธ๋ฅผ ๋ถ์ก์ ์๋ก ๋ง๋ ์์ธ๋ฅผ ๋์ ธ๋ ๋๊ณ , ๊ธฐ์กด ์์ธ๊ฐ ๋ฐ์ํ๊ธฐ ์ ์ index๋ฅผ ๊ฒ์ฌํด ์๋ก ๋ง๋ ์์ธ๋ฅผ ์ง์ ๋ฐ์์์ผ๋ ๊ด์ฐฎ๋ค.
์ ๋ฌํ๋ ์ ๋ณด์ ์์ ์ด ํ์ํ ๋๋ IllegalIndexException ํด๋์ค๋ฅผ ์์ ํ๋ฉด ๋๋ค. ๊ฐ์ ์์ธ๋ฅผ ๋ฐ์์ํค๋ ๋ชจ๋ ์ํฉ์ ์ ์ฉ๋ ๊ฒ์ด๋ค.
- ์์ธ์ ๋ํ ์์ง๋๊ฐ ํฅ์๋๋ค.
- ํด๋์ค๋ฅผ ๋ง๋๋ ํ์๋ ๊ด๋ จ ์ ๋ณด๋ฅผ ํด๋น ํด๋์ค์์ ์ต๋ํ ๊ด๋ฆฌํ๊ฒ ๋ค๋ ์ด์ผ๊ธฐ๋ค.
ํ์ค์์ธ์ ๋ฉ์์ง๋ก๋ ์ถฉ๋ถํ ์ ๋ณด๋ฅผ ์ ๋ฌํ ์ ์์ง๋ง, ์ ๋ฌํ๋ ์ ๋ณด์ ์์ด ๋ง์์ง๊ฑฐ๋ ์ฌ๋ฌ๊ณณ์์ ๋ฐ์ํ๋ ์์ธ๋ผ๋ฉด ์ฑ ์์์ฌ๊ฐ ๋ถ๋ถ๋ช ํด์ง๋ค.
์ฌ์ค ์ ์ ๋ฉ์๋๋ฅผ ๋ด์ ์ ํธ์ฑ ํด๋์ค๋ก๋ ์ถฉ๋ถํ ํ์ค ์์ธ๋ฅผ ์ฌ์ฉํ๋ฉด์ ์ด๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ๋ ์์ง๋ง ์ฌ์ฉ์ ์ ์ ์์ธ๋ฅผ ์ฌ์ฉํ๋ค๋ฉด ๊ฐ์ฒด์ ์ฑ ์์ด ๋ถ๋ฆฌ๋ ๊น๋ํ ์ฝ๋๋ฅผ ์ป์ ์ ์๋ค.
- ์์ธ ๋ฐ์ ํ์ฒ๋ฆฌ๊ฐ ์ฉ์ดํ๋ค.
- ์์ธ๋ ์์ ๊ด๊ณ์ ์๊ธฐ ๋๋ฌธ์, Exception์ด๋ RuntimeException์ ์ก์๋๋ฉด ํ๋ก๊ทธ๋จ ๋ด์์ ๋ฐ์ํ๋ ๊ฑฐ์ ๋ชจ๋ ์์ธ์ ๋ํด ์ฒ๋ฆฌ๊ฐ ๊ฐ๋ฅํ๋ค. ํ์ง๋ง ์ด๋ ํ๋ก๊ทธ๋๋จธ๊ฐ ์๋ํ์ง ์์ ์์ธ๊น์ง ๋ชจ๋ ์ก์๋ด ํผ๋์ ์ผ๊ธฐํ ์ ์๋ค.
์ฌ์ฌ์ฉ์ฑ์ด ๋์ ๊ฒ์ ํ์ค ์์ธ๋ค์ ์ฅ์ ์ด๋ค. ํ์ง๋ง ๊ทธ ์ฅ์ ๋๋ฌธ์ ๋ฐ์ ์์น๋ฅผ ์ ํํ๊ฒ ํ์ ํ๊ธฐ ํ๋ค๋ค๋ ๋จ์ ๋ ์๊ธด๋ค.
- ์์ธ ์์ฑ ๋น์ฉ์ ์ ๊ฐํ๋ค.
- ์๋ฐ์์ ์์ธ๋ฅผ ์์ฑํ๋ ํ์๋ ์๊ฐ๋ณด๋ค ๋ง์ ๋น์ฉ์ด ์๋ชจ๋๋ค. ๋ฐ๋ก stack trace ๋๋ฌธ์ด๋ค.
stack trace๋ ์์ธ ๋ฐ์ ์ call stack์ ์๋ ๋ฉ์๋ ๋ฆฌ์คํธ๋ฅผ ์ ์ฅํ๋ค. ์ด๋ฅผ ํตํด ์์ธ๊ฐ ๋ฐ์ํ ์ ํํ ์์น๋ฅผ ํ์ ํ ์ ์๋ค. ํ์ง๋ง try/catch๋ Advice๋ฅผ ํตํด ์์ธ๋ฅผ ์ฒ๋ฆฌํ๋ค๋ฉด ํด๋ค ์์ธ์ stack trace๋ ์ฌ์ฉํ์ง ์์ ๋๊ฐ ๋ง๋ค. ๋น์ฉ์ ๋ค์ฌ ๋ง๋ค์๋ค์ง๋ง ์ฌ์ฉํ์ง ์๊ณ ์ฌ๋ผ์ง๋ ํํ, ๋๋ฌด๋๋ ๋น์์จ์ ์ด๋ค.
stack trace์ ์์ฑ์ ์์ธ์ ๋ถ๋ชจ ํด๋์ค ์ค Throwable์ fillInStackTrace() ๋ฉ์๋๋ฅผ ํตํด ์ด๋ฃจ์ด์ง๋ค. ์ฌ์ฉ์ ์ ์ ์์ธ๋ ํด๋น ๋ฉ์๋๋ฅผ Override ํจ์ผ๋ก stack trace์ ์์ฑ ๋น์ฉ์ ์ค์ผ ์ ์๋ค. ํ์ํ๋ค๋ฉด ์งง๊ฒ ์ผ๋ถ๋ง์ ์์ฑํ ์๋, ์์ ์์ฑํ์ง ์์ ์๋ ์๋ค.
@Override
public synchronized Throwable fillInStackTrace() {
return this;
}
public class CustomException extends RuntimeException {
public static final CustomException CUSTOM_EXCEPTION = new CustomException("๋์ถฉ ์์ธ๋ผ๋ ๋ด์ฉ");
//...
}
- ๋ง์ผ ๊ตฌํํด ๋ธ Custom Exception์ด stack trace๋ ๊ฐ์ง ์๊ณ , ์ํฉ์ ๋ฐ๋ผ ์ ๋ณด๋ฅผ ๋ค๋ฅด๊ฒ ์ฃผ๋ ์์ธ๊ฐ ์๋๋ผ ๋จ์ํ๊ฒ ๋ฉ์ธ์ง๋ง ๋๊ฒจ์ค๋ค๋ฉด ํด๋น ์์ธ๋ฅผ ์บ์ฑํด ๋๋ ๊ฒ๋ ๋น์ฉ ์ ๊ฐ์ ๋ฐฉ๋ฒ์ด๋ค.
# 1113 - ์๋ฒ ๋๋ ํ์ (embedded type)
# ์๋ฒ ๋๋ ํ์ (๋ณตํฉ ๊ฐ ํ์ )
์๋ก์ด ๊ฐ ํ์ ์ ์ง์ ์ ์ํด์ ์ฌ์ฉํ ์ ์๋ฐ. JPA์์๋ ์ด๊ฒ์ ์๋ฒ ๋๋ ํ์ (embedded type)์ด๋ผ ํ๋ค. ์ค์ํ ๊ฒ์ ์ง์ ์ ์ํ ์๋ฒ ๋๋ ํ์ ๋ int, String ์ฒ๋ผ ๊ฐ ํ์ ์ด๋ค.
# ์์
- ์๋ฒ ๋๋ ํ์ ์ ์ฌ์ฉํ์ง ์์์ ๋
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
private String name;
// ๊ทผ๋ฌด ๊ธฐ๊ฐ
@Temporal(TemporalType.DATE)
Date startDate;
@Temporal(TemporalType.DATE)
Date endDate;
// ์ง ์ฃผ์ ํํ
private String city;
private String street;
private String zipcode;
// ...
}
- ์๋ฒ ๋๋ ํ์ ์ฌ์ฉ
@Entity
public class Member {
@Id @GeneratedVAlue
private Long id;
private String name;
@Embedded
private Period workPeriod; // ๊ทผ๋ฌด ๊ธฐ๊ฐ
@Embedded
private Address homeAddress; // ์ง ์ฃผ์
}
---
@Embeddable
public class Peroid {
@Temporal(TemporalType.DATE)
Date startDate;
@Temporal(TemporalType/Date)
Date endDate;
// ...
public boolean isWork (Date date) {
// .. ๊ฐ ํ์
์ ์ํ ๋ฉ์๋๋ฅผ ์ ์ํ ์ ์๋ค
}
}
---
@Embeddable
public class Address {
@Column(name="city") // ๋งคํํ ์ปฌ๋ผ ์ ์ ๊ฐ๋ฅ
private String city;
private String street;
private String zipcode;
// ...
}
# ์๋ฒ ๋๋ ํ์ ์ฌ์ฉ ๋ฐฉ๋ฒ
- @Embeddable : ๊ฐ ํ์ ์ ์ ์ํ๋ ๊ณณ์ ํ์
- @Embedded : ๊ฐ ํ์ ์ ์ฌ์ฉํ๋ ๊ณณ์ ํ์
์๋ฒ ๋๋ ํ์ ์ ๊ธฐ๋ณธ ์์ฑ์๊ฐ ํ์
์๋ฒ ๋๋ ํ์ ์ ํฌํจํ ๋ชจ๋ ๊ฐ ํ์ ์ ์ํฐํฐ์ ์๋ช ์ฃผ๊ธฐ์ ์์กดํ๋ฏ๋ก ์ํฐํฐ์ ์๋ฒ ๋๋ ํ์ ๊ด๊ณ๋ฅผ UML๋ก ํํ ํ๋ฉด ์ปดํฌ์ง์ (composition) ๊ด๊ณ๊ฐ ๋๋ค.
ํ์ด๋ฒ๋ค์ดํธ๋ ์๋ฒ ๋๋ ํ์ ์ ์ปดํฌ๋ํธ(components)๋ผ ํ๋ค.
<ํ์-์ปดํฌ์ง์ ๊ด๊ณ UML (์ถ์ฒ: ๊น์ํ๋ ๊ฐ์)>
# ์๋ฒ ๋๋ ํ์ ์ ์ฅ์
- ์ฌ์ฌ์ฉ
- ๋์ ์์ง๋
- Period ๊ฐ์ฒด์ isWork() ๋ฉ์๋์ฒ๋ผ ํด๋น ๊ฐ ํ์ ๋ง ์ฌ์ฉํ๋ ์๋ฏธ์๋ ๋ฉ์๋๋ฅผ ๋ง๋ค ์ ์๋ค.
# ์๋ฒ ๋๋ ํ์ ๊ณผ ํ ์ด๋ธ ๋งคํ
<์๋ฒ ๋๋ ํ์ ์ ์ฌ์ฉํ ํ์-ํ ์ด๋ธ ๋งคํ (์ถ์ฒ: ๊น์ํ๋ ๊ฐ์)>
์๋ฒ ๋๋ ํ์ ์ ์ํฐํฐ์ ๊ฐ์ผ ๋ฟ์ด๋ค. ๋ฐ๋ผ์ ๊ฐ์ด ์ํ ์ํฐํฐ์ ํ ์ด๋ธ์ ๋งคํ๋๋ฉฐ, ์๋ฒ ๋๋ ํ์ ์ ์ฌ์ฉํ๊ธฐ ์ ๊ณผ ํ์ ๋งคํ๋๋ ํ ์ด๋ธ์ ๋์ผํ๋ค.
์๋ฒ ๋๋ ํ์ ๋๋ถ์ ๊ฐ์ฒด์ ํ ์ด๋ธ์ ์์ฃผ ์ธ๋ฐํ๊ฒ(fine-grained) ๋งคํํ๋ ๊ฒ์ด ๊ฐ๋ฅํ๋ค. ์ ์ค๊ณํ ORM ์ ํ๋ฆฌ์ผ์ด์ ์ ๋งคํํ ํ ์ด๋ธ์ ์๋ณด๋ค ํด๋์ค์ ์๊ฐ ๋ ๋ง๋ค.
# ์๋ฒ ๋๋ ํ์ ๊ณผ ์ฐ๊ด๊ด๊ณ
์๋ฒ ๋๋ ํ์ ์ ๊ฐ ํ์ ์ ํฌํจํ๊ฑฐ๋ ์ํฐํฐ๋ฅผ ์ฐธ์กฐํ ์ ์๋ค.
@Entity
public class Member {
@Embedded
Address address; // ์๋ฒ ๋๋ ํ์
ํฌํจ
@Embedded
PhoneNumber phoneNumber; // ์๋ฒ ๋๋ ํ์
ํฌํจ
// ...
}
@Embeddable
public class Address {
String street;
String city;
String state;
@Embedded
Zipcode zipcode; // ์๋ฒ ๋๋ ํ์
ํฌํจ
}
@Embeddable
public class Zipcode {
String zip;
String plusFour;
}
@Embeddable
public class PhoneNumber {
String areaCode;
String localNumber;
@ManyToOne
PhoneServiceProvider provider; // ์ํฐํฐ ์ฐธ์กฐ
}
@Entity
public class PhoneServiceProvider {
@Id
String name;
// ...
}
๊ฐ ํ์ ์ธ Address๊ฐ ๊ฐ ํ์ ์ธ Zipcode๋ฅผ ํฌํจํ๊ณ , ๊ฐ ํ์ ์ธ PhoneNumber๊ฐ ์ํฐํฐ ํ์ ์ธ PhoneServiceProvider๋ฅผ ์ฐธ์กฐํ๋ค.
# @AttributeOverride: ์์ฑ ์ฌ์ ์
์๋ฒ ๋๋ ํ์ ์ ์ ์ํ ๋งคํ์ ๋ณด๋ฅผ ์ฌ์ ์ํ๋ ค๋ฉด ์ํฐํฐ์ @AttributeOverride๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค.
// ๊ฐ์ ์๋ฒ ๋๋ ํ์
์ ๊ฐ์ง๊ณ ์๋ ํ์
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
private String name;
@Embedded
Address homeAddress;
@Embedded
Address companyAddress;
}
์ ์ฝ๋์ ๋ฌธ์ ์ ์ ํ ์ด๋ธ์ ๋งคํํ๋ ์ปฌ๋ผ๋ช ์ด ์ค๋ณต๋๋ค. ์ด๋๋ ์๋์ ๊ฐ์ด @AttributeOberrides๋ฅผ ์ฌ์ฉํด์ ๋งคํ์ ๋ณด๋ฅผ ์ฌ์ ์ํด์ผ ํ๋ค.
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
private String name;
@Embedded
Address homeAddress;
@Embedded
@AttributeOverrides({
@AttributeOverride(name="city", column=@Column(name="COMPANY_CITY")),
@AttributeOverride(name="street", column=@Column(name="COMPANY_STREET")),
@AttributeOverride(name="zipcode", column=@Column(name="COMPANY_ZIPCODE"))
})
Address companyAddress;
}
// ์์ฑ๋ ํ
์ด๋ธ
------------------------------
CREATE TABLE MEMBER (
COMPANY_CITY varchar(255),
COMPANY_STREET varchar(255),
COMPANY_ZIPCODE varchar(255),
city varchar(255),
street varchar(255),
zipcode varchar(255),
...
)
------------------------------
@AttributeOverrides๋ ์ํฐํฐ์ ์ค์ ํด์ผ ํ๋ค. ์๋ฒ ๋๋ ํ์ ์ด ์๋ฒ ๋๋ ํ์ ์ ๊ฐ์ง๊ณ ์์ด๋ ์ํฐํฐ์ ์ค์ ํด์ผ ํ๋ค.
# ์๋ฒ ๋๋ ํ์ ๊ณผ Null
์๋ฒ ๋๋ ํ์ ์ด null์ด๋ฉด ๋งคํํ ์ปฌ๋ผ ๊ฐ์ ๋ชจ๋ null์ด ๋๋ค.