Scala, Spring-Boot, JPA를 활용한 웹 애플리케이션 개발 과정에 대해 다룬다. Spring-Boot와 JPA 조합만으로도 생산성 있는 웹 애플리케이션 개발이 가능하다. 이 조합만으로도 충분히 의미가 있지만 여기에 Scala라는 약간은 불편한 듯 보이는 언어를 도입함으로써 얻을 수 있는 즐거움을 공유한다. Spring-Boot + JPA 조합에 Scala를 적용하면서의 좌충우돌 경험담을 전한다.
12. JPA
• 단순, 반복적인 CRUD 작업하기 싫다.
• 스키마 변경에 따른 비용을 최소화하고 싶다.
• 핵심 비즈니스 로직에 집중하고 싶다.
13. JPA 도입에 따른 더 큰 효과
요구사항이 자주 변경되는 프로젝트 초반, 빠른 사이클로 설계에
대해 다양한 실험이 가능하다.
14. JPA를 사용하지 않는 경우 – 테이블 최초 생성시
데이터베이스에 스키마 적용
비즈니스 로직 구현 테이블 설계
테이블 스키마 생성SQL 쿼리 구현
자바 테이블과 매핑되는 객체 추
가
1
2
34
5
6
15. JPA를 사용하지 않는 경우 – 테이블 스키마 변경시
데이터베이스에 스키마 적용
비즈니스 로직 변경(선택)
테이블 설계
테이블 스키마 변경
칼럼 변경에 따른 SQL
쿼리 수정
자바 객체에 필드 변경
16. 비즈니스 로직 구현 객체 설계
JPA를 사용하는 경우 – 객체(Entity) 최초 생성시
객체 추가 및 매핑
데이터베이스에 스
키마 적용
비즈니스 로직 구현 테이블 설계
테이블 스키마 생성SQL 쿼리 구
현
자바 테이블과 매핑되
는 객체 추가
1
2
3
17. 비즈니스 로직 구현(선
택)
객체 설계
JPA를 사용하는 경우 – 객체(Entity) 필드 변경
객체 필드 변경
데이터베이스에 스키마
적용
비즈니스 로직 변경(선택)
테이블 설계
테이블 스키마 변경칼럼 변경에 따른
SQL 쿼리 수정
자바 객체에 필드 변경
18. JPA 도입 효과
• 요구사항이 자주 변경되는 프로젝트 초반 빠른 구현 – 피드백 사이
클
• 빠른 피드백 사이클은 삽질할 수 있는 시간을 확보함으로써 빠른 지식
축적이 가능하다.
• 지식 축적은 도메인에 최적화된 설계를 할 수 있도록 한다.
• 좋은 설계는 사용자의 요구사항 변화에 빠르게 대응할 수 있다.
• 개발자는 소스 코드에 대한 자부심과 여유 시간을 확보할 수 있다.
22. 1. @IntegrationTest
• Spring Boot는 Embedded Tomcat 기반으로 동작.
• @IntegrationTest 기반으로 테스트하면 Embedded Tomcat을 자동
실행한 후 Client 테스트가 가능.
• 특히 RestTemplate을 활용해 API 테스트할 때 유용.
28. 제공하는 정보
• Spring 관련 정보
• /autoconfig – Spring auto configuration 정보
• /beans - Spring Bean 목록
• /configprops - @ConfigurationProperties 정보
• /mappings - @RequestMapping 정보
• 시스템 운영 및 성능
• /dump – thread dump
• /health – 애플리케이션의 정상 동작 유무
• /metrics – 애플리케이션의 메모리 상태, heap, thread 등과 관련한 정보 제공
• http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#production-
ready 에서 추가 정보 확인
35. Scala
• 10년 이상 Java만 했더니 재미없더라.
• 2년 정도 함수형 언어로 Scheme, Clojure 도전했다가 중도 포기
• 어딘가 직접적으로 활용하고 있지 않으면 동기부여가 잘 되지 않는
성격
36. 1. Domain과 DTO의 명확한 분리에 대한 거부감이
줄어듦
• 현재 개발 추세는 Domain 객체와 DTO에 중복되는 부분이 많아 자바
객체 하나가 Domain 역할, DTO 역할을 하는 방식으로 구현.
• Scala를 활용하면 각 역할별로 구현하는 것에 대한 거부감이 줄어듦
37. @Entity
class User(pEmail: String, pNickName: String, pPassword: String) extends
DomainModel {
@Id
@GeneratedValue
var id: Long = _
@Column(unique = true, nullable = false)
val email = pEmail
@Column(name = "nick_name", nullable = false)
val nickName = pNickName
@Column(nullable = false)
val password = pPassword
def isGuest(): Boolean = {
false
}
}
User Entity
• 반드시 setter/getter를 생성하지 않아도 된다.
38. @JsonIgnoreProperties(ignoreUnknown = true)
@JsonPropertyOrder(alphabetic = true)
@JsonInclude(Include.NON_NULL)
@JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility =
Visibility.NONE, setterVisibility = Visibility.NONE)
trait View
case class UserView(id: Long, email: String, nickName: String) extends
View {
def this(u: User) = this(u.id, u.email, u.nickName)
def this() = this(new User())
}
User View DTO
• Scala case class를 활용하면 자동으로 field 추가함.
39. class UserForm {
@BeanProperty
@Email
var email: String = _
@BeanProperty
@NotNull
@Size(min = 3, max = 10)
var nickName: String = _
@BeanProperty
@NotNull
@Size(min = 8, max = 15)
var password: String = _
def toUser() = new User(email, nickName,
password)
}
User Form DTO
• @BeanProperty 활용하면 setter/getter method 자동
추가
40. Domain과 DTO의 명확한 분리에 대한 거부감이
줄어듦
• 분리하는 것이 항상 좋은 것은 아니다.
• 상황에 따라 Entity와 DTO를 분리/통합할 것인지에 대한
역량을 키우는 것이 더 중요하다.
41. 2. Test Fixture(Test Data) 생성하기 용이함.
• 자바에서 Test Fixture를 생성하고 변경하기 어려움은 Test 코드를
만드는데 약간의 장애물이다.
• Scala는 named parameter를 통해 해결 가능
43. public class UserBuilder {
private String email;
private String nickname;
private String password;
public UserBuilder withEmail(String email) {
this.email = email;
return this;
}
public UserBuilder withNickname(String nickname) {
this.nickname = nickname;
return this;
}
public UserBuilder withPassword(String password) {
this.password = password;
return this;
}
public User build() {
return new User(email, nickname, password);
}
}
44. public class UserTest {
@Test
public void canCreate() throws Exception {
User user1 = new UserBuilder().withEmail("some@sample.com").build();
User user2 = new
UserBuilder().withEmail("some@sample.com").withNickname("newname").build();
}
}
22장. 복잡한 테스트 데이터 만들기 참고
45. trait Fixture {
def aSomeUser(email: String = "some@sample.com", nickname: String = "nickName", password:
String = "password")
= new User(email, nickname, password)
}
val user1 = aSomeUser
val user2 = aSomeUser(email="some2@sample.com")
val user1 = aSomeUser(nickName="newname")
46. 3. implicit을 활용한 중복 제거
• 애플리케이션을 구현하다보면 반복적으로 전달하는 인자가 존재함.
• Scala의 implicit을 활용해 제거 가능