SlideShare une entreprise Scribd logo
1  sur  91
source of slipp.map(java => scala)
Scala를 선택하기까지의 삽질 경험담
Spring + JPA + Scala 조합을 선택한 이유
SLiPP 서비스를 Java에서 Scala로 전환하는 과정
Scala 전환을 통해 얻는 이점
Spring + JPA + Scala 조합시 고려할 사항
Scala를 선택하기까지의 삽질 경험담
40대를 넘어선 2013년의 어느 날
• 10년 이상 Java만 했더니 재미없다.
• 최근에 함수형 언어가 대세라더라.
• 삶이 무료하다. 뭔가 재미있는 일 좀 있으면 좋겠다.
• 40대가 되면 생긴다는 그 병
일단 시작하는 거 제대로 공부하자.
• Scheme이라는 완전히 생소한 언어에 도전
• 내용 진짜 어려움
• 실무에 적용할 곳이 보이지 않음.
• 서서히 동기부여가 되지 않으면서 중도 포기.
• Scheme이라는 완전히 생소한 언어에 도전
• 내용 진짜 어려움
• 실무에 적용할 곳이 보이지 않음.
• 서서히 동기부여가 되지 않으면서 중도 포기.
하지만 함수형 언어가 무엇인지에 대한 대략적인 감은 잡음
2013년 말 다시 병이 도짐
• 최근에 함수형 언어가 대세라더라.
• 삶이 무료하다. 뭔가 재미있는 일 좀 있으면 좋겠다.
• JVM 기반 함수형 언어를 학습하면
실무 적용 가능할거야.
• Clojure는 Scheme과 문법이
비슷하잖아.
• SLiPP 스터디에 Clojure 스터디 진행
• 스터디가 항상 그렇듯이 초반
3주차까지 좋았음.
• 4주차 이후부터 스터디원이 거의
나오지 않음.
• 스터디 중도 포기. 지금까지 여러
번의 SLiPP 스터디를 진행했지만
중도 포기한 유일한 스터디
• 나도 실무에 적용할 방법을 찾지 못함
하지만 스터디원 중의 한 명은 클로저 빠로 만듦
2014년 ~ 2015년 초반
• 마흔이 넘어 무슨 함수형 언어야.
• 그냥 자바로 15년만 더 버티자.
• 그러던 어느 날 스터디에 Scala를 하잖다.
• 함수형의 극한을 맛보는 경험을 포기하고 현실과 타협하자.
• 나란 인간은 실무에 적용하지 않으면 지속적인 동기부여를
하기 힘들다.
• 욕심을 버리고 Scala로 가자.
• 스스로 자위하면서 스터디에 합류.
• 아직도 함수형 프로그래밍에 대한
욕심을 버리지 못함.
• 책 무지 좋다. 단, Scala 문법도
익숙하지 않은 초보자가 보기 너무
어려움.
• 스터디 초반 교재로 고민하다 포기함.
• 돌고 돌아 함수형 프로그래밍에 대한
욕심을 버리고 Programming in
Scala
• 욕심을 버렸는데 재미가 없다.
Scala도 스터디 끝나면 기억 속에서
사라질 거 같다.
• SLiPP을 Java에서 Scala로 전면
개편하자.
느낀 점
• 2013년 무작정 시작했던 함수형 프로그래밍에 대한 도전이 Scala를
통해 결실을 맺다.
• 너무 많은 것을 고려한 후 실행에 옮기기 보다 무작정 실행해 보는
것도 삶에 새로운 길을 만든다.
Spring + JPA + Scala 조합을 선택
• 돌고 돌아 함수형 프로그래밍에 대한 욕심을 버리고
Programming in Scala
• 욕심을 버렸는데 재미가 없다. Scala도 스터디 끝나면 기억
속에서 사라질 거 같다.
• SLiPP을 Java에서 Scala로 전면 개편하자.
• 돌고 돌아 함수형 프로그래밍에 대한 욕심을 버리고
Programming in Scala
• 욕심을 버렸는데 재미가 없다. Scala도 스터디 끝나면 기억
속에서 사라질 거 같다.
• SLiPP을 Java에서 Scala로 전면 개편하자.
Play가 아닌 Scala + Spring + JPA 조합을 선택한
이유
• SLiPP 서비스를 처음부터 다시 구현하는 것에 대한 부담감
• Spring과 JPA 경험을 버리는 것의 아쉬움.
• 중도 포기하지 않으려면 변화를 Scala 하나로 한정.
“나름 생산성 높다.”
라는 자기 합리화
귀찮다.
마흔이 넘어보면 알 수 있다.
source of slipp.map(java => scala)
2015년 5월 22일 시작
2015년 12월 30일 끝
Scala 적용(리팩토링) 원칙
• 기존 기능을 서비스하면서 점진적으로 리팩토링한다.
• 리팩토링 단계에 컴파일 에러가 발생하는 시간을 최소화한다.
1단계 – Java와 Scala 동시 개발 환경
구축
• Java와 Scala 코드가 동시에 컴파일과 실행이 가능하도록 환경
구축함.
• Java 소스 코드
• src/main/java, src/test/java
• Scala 소스 코드
• src/main/scala, src/test/scala
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scala.version}</version>
</dependency>
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>3.2.1</version>
<configuration>
<recompileMode>incremental</recompileMode>
<javacArgs>
<javacArg>-Xlint:unchecked</javacArg>
<javacArg>-Xlint:deprecation</javacArg>
</javacArgs>
<source>${jdk.compiler.version}</source>
<target>${jdk.compiler.version}</target>
</configuration>
<executions>
<execution>
<id>scala-test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
<execution>
<phase>process-resources</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
• 10년 이상 이클립스 기반으로
살아왔다.
• 이클립스에서 Scala IDE 활용해
Scala 개발 시작하는 것은 당연한
수순이었다.
• 이클립스 버리고 싶지 않았는데 몇 달
후 GG
• 이클립스 버리고 싶지 않았다.
• 하지만 Scala는 Intellij의 Scala
플러그인이 갑이다.
• Community 버전에서도 무료로 사용
가능하다.
2단계 – 위험 요소가 높은 부분부터
실험
• Java 기반의 SLiPP 코드는 Spring + JPA(Spring Data JPA) 구조
• JPA 기반 Entity를 Scala로 개발 가능한지 실험
• 다른 Entity와 가장 의존관계가 적은 기능을 Scala로 먼저 변경
• JPA Entity 실험과 리팩토링 원칙에 대한 경험을 같이 진행
package net.slipp.domain.tag;
[...]
@Entity
@Table(indexes = {
@Index(name = "idx_tagged_history_tag", columnList="tag_id"),
@Index(name = "idx_tagged_history_question", columnList="question_id")}
)
@EntityListeners({ CreatedDateEntityListener.class })
public class TaggedHistory implements HasCreatedDate {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long historyId;
@Column(name = "tag_id", nullable = false, updatable = false)
private Long tagId;
@Enumerated(EnumType.STRING)
@Column(name = "tagged_type", nullable = false, updatable = false, columnDefinition =
TaggedType.COLUMN_DEFINITION)
private TaggedType taggedType;
public TaggedHistory(Long tagId, Long questionId, Long userId, TaggedType taggedType) {
this.tagId = tagId;
this.questionId = questionId;
this.userId = userId;
this.taggedType = taggedType;
}
[...]
}
Scala 적용(리팩토링) 원칙
• 기존 기능을 서비스하면서 점진적으로 리팩토링한다.
• 리팩토링 단계에 컴파일 에러가 발생하는 시간을 최소화한다.
자바 리팩토링 과정과 같은 방식으로
Java => Scala 전환 과정 설계
public abstract class JdbcTemplate {
public void update() throws SQLException {
[…]
}
public abstract String createQuery();
public abstract void setValues(PreparedStatement pstmt) throws SQLException;
}
리팩토링 전
public abstract class JdbcTemplate {
public void update() throws SQLException {
[…]
}
public void update(String sql) throws SQLException {
[…]
}
public abstract String createQuery();
public abstract void setValues(PreparedStatement pstmt) throws SQLException;
}
리팩토링 중(과도기 단계)
public abstract class JdbcTemplate {
public void update(String sql) throws SQLException {
[…]
}
public abstract void setValues(PreparedStatement pstmt) throws SQLException;
}
리팩토링 후
Scala 적용(리팩토링) 원칙
• 기존 기능을 서비스하면서 점진적으로 리팩토링한다.
• 리팩토링 단계에 컴파일 에러가 발생하는 시간을 최소화한다.
• 리팩토링 전과 후의 코드가 공존하는 단계가 반드시 필요하다.
• 이 같은 전략은 소스 코드 리팩토링 뿐 아니라 Java => Scala 전환, DB 리팩토링 또한 같다.
• 리팩토링 전과 후의 결과를 쉽게 테스트할 수 있어야 한다.
Java => Scala 전환 과정 설계(예, TaggedHistory)
• TaggedHistory.java => NTaggedHistory.scala
• Scala로 변경한 코드에서 생성된 Table Schema와 기존 자바 코드에서 생성된 Table Schema가
같은지 검증한다. Table Schema가 같은 시점을 Entity 변환 완료 시점으로 가정한다.
• Table Schema를 검증하기 위한 테스트 도구가 필요.
• TaggedHistory를 사용하는 코드를 NTaggedHistory를 사용하도록 변경한다.
• 테스트한다.
• TaggedHistory Entity를 삭제한다.
• NTaggedHistory.scala => TaggedHistory.scala로 rename
package net.slipp.ndomain.tag
[...]
@Entity(name="TaggedHistory")
@Table(indexes = Array(
new Index(name = "idx_tagged_history_tag", columnList="tag_id"),
new Index(name = "idx_tagged_history_question", columnList="question_id")))
class NTaggedHistory(t: Long, q: Long, u: Long, tType: String) extends DomainModel with NHasCreatedDate
{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
var historyId: Long = _
@Column(name = "tag_id", nullable = false, updatable = false)
val tagId = t
@Column(name = "tagged_type", nullable = false, updatable = false, columnDefinition =
NTaggedType.ColumnDefinition)
val taggedType = tType
def this() = this(0L, 0L, 0L, null)
}
매핑 중 삽질 내용 1
• Scala Annotation에서 배열을 사용하는 경우 기존 Java Annotation 배열({})을 사용할 수 없었다.
Scala Annotation 배열은 Array
package net.slipp.ndomain.tag
[...]
@Entity(name="TaggedHistory")
@Table(indexes = Array(
new Index(name = "idx_tagged_history_tag", columnList="tag_id"),
new Index(name = "idx_tagged_history_question", columnList="question_id")))
class NTaggedHistory(t: Long, q: Long, u: Long, tType: String) extends DomainModel with NHasCreatedDate
{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
var historyId: Long = _
@Column(name = "tag_id", nullable = false, updatable = false)
val tagId = t
@Column(name = "tagged_type", nullable = false, updatable = false, columnDefinition =
NTaggedType.ColumnDefinition)
val taggedType = tType
def this() = this(0L, 0L, 0L, null)
}
매핑 중 삽질 내용 2
• 매핑 과정 중 해결하지 못한 문제는 java enum을 활용해 Mapping하는 부분이다. Scala에서도
Enumeration을 사용하는 Enum이 있지만 java의 enum과는 동작방식이 달라 그대로 사용할 수
없다.
• 1차 해결 방법은 Scala Entity에서 java enum을 사용하도록 한다. Scala 코드와 Java 코드가
섞여서 사용되는 구조가 된다.
• 2차 해결 방법은 Scala Enum 또는 Scala Case Object를 활용해 해결해야 한다. 이 경우
Entity를 사용하는 Java 코드에서 Scala 클래스에 접근하지 못하는 이슈와 Entity 내부에서
매핑을 위한 변환 작업이 필요하다.
실험을 통한 결론 및 얻게 된 경험
• Scala 기반으로 JPA 기반 개발 가능.
• Scala => Java API 접근은 문제 없음. But, Java => Scala API 접근에 한계가 많음을 느낌.
• Controller => Service => Repository + Entity 순서로 리팩토링 전략 수립함.
3단계 – Layer별로 Scala 전환
• Controller부터 Scala로 리팩토링 시작
• 리팩토링 과정
• src/main/scala의 같은 package에 NHomeController.scala를 생성
• NHomeController로 URL 하나씩 이동하면서 관련된 method 이전함.
• 컴파일 에러가 없는 상태로 만든 후 HomeController.java에서 Controller Annotation 제거함.
• 웹 서버 시작해 기능이 정상적으로 동작하는지 테스트
• 정상 동작을 확인하면 HomeController.java를 제거함.
• NHomeController 파일을 HomeController로 rename 리팩토링 진행함.
• 모든 Controller에 대해 무한 반복
지루하고 짜증나는 반복 과정의 연속
속도가 잘 나지 않는다.
방법 1.
IntelliJ의 신세계 경험
Java 소스 코드를 Scala 소스 코드로 자동 변환해 준다.
방법 2.
일단 모든 Java 코드를 Scala 코드로 전환하는 것을 1차 목표로 한다.
Scala 코드로 전환하는 과정에서 절대 리팩토링을 하지 않는다.
토할 정도로 Java 코드를 Scala 코드로 전환하는 것에만 집중.
4단계(ing) –Scala 스타일로 리팩토링
private String sendMessageToFacebook(SocialUser loginUser, String link, String receiverId, String
message) {
String postId = null;
try {
FacebookClient facebookClient = createFacebookClient(loginUser);
int i = 0;
do {
if (i > 2) {
break;
}
FacebookType response = facebookClient.publish(receiverId + "/feed", FacebookType.class,
Parameter.with("link", link), Parameter.with("message", message));
postId = response.getId();
i++;
} while (postId == null);
log.debug("connect post id : {}", postId);
} catch (Throwable e) {
log.error("Facebook Connection Failed : {}", e.getMessage());
}
return postId;
}
리팩토링 사례 1 – java 코드
@annotation.tailrec
private def retry[T](n: Int)(fn: => T): T = {
Try { fn } match {
case Success(x) => x
case _ if n > 1 => retry(n - 1)(fn)
case Failure(e) => throw e
}
}
리팩토링 사례 1 – scala 코드
private def sendMessageToFacebook(loginUser: SocialUser, link: String, receiverId: String,
message: String) = {
retry(3) {
val facebookClient: FacebookClient = createFacebookClient(loginUser)
val response: FacebookType = facebookClient.publish(receiverId + "/feed",
classOf[FacebookType], Parameter.`with`("link", link), Parameter.`with`("message",
message))
response.getId
}
}
public Set<Tag> processGroupTags(Set<FacebookGroup> groupTags) {
Set<Tag> tags = Sets.newHashSet();
for (FacebookGroup each : groupTags) {
if (each.isEmpty()) {
continue;
}
Tag tag = tagRepository.findByGroupId(each.getGroupId());
if (tag != null) {
tags.add(tag);
continue;
}
tag = tagRepository.findByName(each.getName());
if (tag != null) {
tag.moveGroupTag(each.getGroupId());
tags.add(tag);
continue;
}
Tag newTag = Tag.groupedTag(each.getName(), each.getGroupId());
tags.add(tagRepository.save(newTag));
}
return tags;
}
리팩토링 사례 2 – java 코드
Option(tagRepository.findByGroupId(facebookGroup.getGroupId))
.orElse(Option(tagRepository.findByName(facebookGroup.getName))
.map(t => t.moveGroupTag(facebookGroup.getGroupId)))
.getOrElse(tagRepository.save(Tag.groupedTag(facebookGroup.getName,
facebookGroup.getGroupId)))
리팩토링 사례 2 – scala 코드
지금까지 내용 정리
Java에서 Scala 전환 단계
• 1단계 : Java와 Scala를 같이 실행할 수 있는 환경을 구축한다.
• 2단계 : Scala 전환시 위험요소가 있다고 판단되는 부분이 있다면
이에 대한 실험을 먼저 진행하고 전략을 세운다.
• 3단계 : 앞에서 세운 전략에 따라 Scala 전환 작업을 진행한다.
• 4단계 : Scala 스타일로 리팩토링한다.
Scala를 통해 얻는 이점
1. Domain과 DTO의 명확한 분리에 대한 거부감이
줄어듦
• 현재 개발 추세는 Domain 객체와 DTO에 중복되는 부분이 많아 자바
객체 하나가 Domain 역할, DTO 역할을 하는 방식으로 구현.
• Scala를 활용하면 각 역할별로 구현하는 것에 대한 거부감이 줄어듦
@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를 생성하지 않아도 된다.
@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 추가함.
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 자동
추가
Domain과 DTO의 명확한 분리에 대한 거부감이
줄어듦
• 분리하는 것이 항상 좋은 것은 아니다.
• 상황에 따라 Domain과 DTO를 분리/통합할 것인지에 대한
역량을 키우는 것이 더 중요하다.
2. Test Fixture(Test Data) 생성하기 용이함.
• 자바에서 Test Fixture를 생성하고 변경하기 어려움은 Test 코드를
만드는데 약간의 장애물이다.
• Scala는 named parameter를 통해 해결 가능
email, nickname, password를 가지는 User 객체에 대한 테스트를 데이터를
만든다면…
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);
}
}
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장. 복잡한 테스트 데이터 만들기 참고
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")
• Case Class
• Named Parameter
• Implicit
• Some/Option
• Lambda(람다)
• 막강 Collection
• 모나드 등등 …
• Functional Programming
• Case Class
• Named Parameter
• Implicit
• Some/Option
3. 삶의 무료함을 치유
• 끊임없는 리팩토링 경험
• 나름 생산성 높음
• 객체 지향과 함수형 프로그래밍의 절묘한 조화
• 시대에 뒤쳐지지 않는 듯한 나만의 착각
4. 덤으로 swift 학습
• Scala를 사용했더니 swift 프로그래밍이 껌이다.
Scala 및 Scala + Spring + JPA 기반으로
개발할 때 고려할 부분
Scala + Spring + JPA 조합에서 불편한 부분
• Java 기반 프레임워크 사용하면서 고려할 부분이 생긴다.
• Scala의 모든 기능을 극한으로 사용하는데 제약 사항이 있다.
@RestController
class UserController @Autowired() (val userRepository: UserRepository) {
val Log = LoggerFactory.getLogger(classOf[UserController])
@RequestMapping(value = Array("/users"), method = Array(RequestMethod.POST) )
def join(@Valid @RequestBody userForm: UserForm, result: BindingResult) = {
...
}
@RequestMapping(value = Array("/users/{userEmail}"), method =
Array(RequestMethod.POST) )
def login(@PathVariable userEmail: String, @RequestParam(required = true)
password:String) = {
...
}
}
import java.lang.Long
import org.springframework.data.repository.CrudRepository
trait UserRepository extends CrudRepository[User, Long] {
def findByEmail(email: String): User
def findByEmailAndPassword(email: String, password: String): User
def findByNickName(nickName: String): User
}
import java.util.{ArrayList, List}
@Entity
class Clan(pName: String) extends DomainModel {
...
@OneToMany(mappedBy = "clan", cascade = Array(CascadeType.PERSIST,
CascadeType.REMOVE), fetch = FetchType.LAZY)
val clanMembers: List[ClanMember] = new ArrayList[ClanMember]
}
Scala 컴파일 시간
• Scala의 가장 큰 단점은 컴파일 시간이 많이 소요된다.
• Scala 기반으로 개발하려면 SSD는 필수. 장비빨이 받쳐주어야 한다.
Scala 학습 비용이 높다.
• 한 번에 Scala의 모든 기능을 활용하려고 하기 때문이다.
Scala 적용 단계
1. Scala를 자바처럼 구현한다.
2. 점차 Scala 문법에 친숙해지면 Scala 기능을 하나씩 적용한다.
3. 함수형 프로그래밍 스타일로 구현한다.
마치며…
Java만 너무 오래 했더니 재미없다. 새로운 언어에 도전해 보고 싶다.
그런데 그 동안 쌓아온 경험을 버리기는 아깝다.
Scala가 대안이 될 수 있다.
최근 함수형 프로그래밍이 뜬다더라. 새로운 언어를 도입해 함수형
프로그래밍을 학습하고 싶지만 현재 운영 중인 시스템이 Java 환경이다.
Scala가 대안이 될 수 있다.
프로젝트를 새로 시작한다면 play
framework도 대안
대부분의 개발자가 Scala에 익숙하지
않고, Spring 프레임워크에 친숙하다면
Scala + Spring 조합도 괜찮은 조합이
대안이 될 수 있다.
자바 프로그래머로 15년 이상 살아온 나에게는 2016년 현재
Spring-Boot, JPA, Scala가 최선의 조합이고, 흥미로운 조합이라 생각
하지만 Swift로 백엔드 프로그래밍이 가능하다면
1년 후에는 자바 생태계를 떠날지도…

Contenu connexe

Tendances

자바프로그래머를 위한 스칼라
자바프로그래머를 위한 스칼라자바프로그래머를 위한 스칼라
자바프로그래머를 위한 스칼라Jong Gook Bae
 
Effective Scala (SpringCamp 2013)
Effective Scala (SpringCamp 2013)Effective Scala (SpringCamp 2013)
Effective Scala (SpringCamp 2013)ihji
 
Scala, Spring-Boot, JPA의 불편하면서도 즐거운 동거
Scala, Spring-Boot, JPA의 불편하면서도 즐거운 동거Scala, Spring-Boot, JPA의 불편하면서도 즐거운 동거
Scala, Spring-Boot, JPA의 불편하면서도 즐거운 동거Javajigi Jaesung
 
자바9 특징 (Java9 Features)
자바9 특징 (Java9 Features)자바9 특징 (Java9 Features)
자바9 특징 (Java9 Features)Chang-Hwan Han
 
[162] jpa와 모던 자바 데이터 저장 기술
[162] jpa와 모던 자바 데이터 저장 기술[162] jpa와 모던 자바 데이터 저장 기술
[162] jpa와 모던 자바 데이터 저장 기술NAVER D2
 
Ksug2015 jpa5 스프링과jpa
Ksug2015 jpa5 스프링과jpaKsug2015 jpa5 스프링과jpa
Ksug2015 jpa5 스프링과jpaYounghan Kim
 
SpringDataJPA - 스프링 캠프
SpringDataJPA - 스프링 캠프SpringDataJPA - 스프링 캠프
SpringDataJPA - 스프링 캠프Younghan Kim
 
Ksug2015 - JPA2, JPA 기초와매핑
Ksug2015 - JPA2, JPA 기초와매핑Ksug2015 - JPA2, JPA 기초와매핑
Ksug2015 - JPA2, JPA 기초와매핑Younghan Kim
 
Hibernate start (하이버네이트 시작하기)
Hibernate start (하이버네이트 시작하기)Hibernate start (하이버네이트 시작하기)
Hibernate start (하이버네이트 시작하기)visual khh
 
Ksug2015 jpa4 객체지향쿼리
Ksug2015 jpa4 객체지향쿼리Ksug2015 jpa4 객체지향쿼리
Ksug2015 jpa4 객체지향쿼리Younghan Kim
 
Spring data jpa
Spring data jpaSpring data jpa
Spring data jpaTaesin Um
 
자바야 놀자 PPT
자바야 놀자 PPT자바야 놀자 PPT
자바야 놀자 PPTJinKyoungHeo
 
프론트엔드스터디 E03 - Javascript intro.
프론트엔드스터디 E03 - Javascript intro.프론트엔드스터디 E03 - Javascript intro.
프론트엔드스터디 E03 - Javascript intro.Young-Beom Rhee
 
영속성 컨텍스트로 보는 JPA
영속성 컨텍스트로 보는 JPA영속성 컨텍스트로 보는 JPA
영속성 컨텍스트로 보는 JPA경원 이
 
좌충우돌 ORM 개발기 | Devon 2012
좌충우돌 ORM 개발기 | Devon 2012좌충우돌 ORM 개발기 | Devon 2012
좌충우돌 ORM 개발기 | Devon 2012Daum DNA
 

Tendances (20)

자바프로그래머를 위한 스칼라
자바프로그래머를 위한 스칼라자바프로그래머를 위한 스칼라
자바프로그래머를 위한 스칼라
 
Effective Scala (SpringCamp 2013)
Effective Scala (SpringCamp 2013)Effective Scala (SpringCamp 2013)
Effective Scala (SpringCamp 2013)
 
Scala, Spring-Boot, JPA의 불편하면서도 즐거운 동거
Scala, Spring-Boot, JPA의 불편하면서도 즐거운 동거Scala, Spring-Boot, JPA의 불편하면서도 즐거운 동거
Scala, Spring-Boot, JPA의 불편하면서도 즐거운 동거
 
자바9 특징 (Java9 Features)
자바9 특징 (Java9 Features)자바9 특징 (Java9 Features)
자바9 특징 (Java9 Features)
 
[162] jpa와 모던 자바 데이터 저장 기술
[162] jpa와 모던 자바 데이터 저장 기술[162] jpa와 모던 자바 데이터 저장 기술
[162] jpa와 모던 자바 데이터 저장 기술
 
Ksug2015 jpa5 스프링과jpa
Ksug2015 jpa5 스프링과jpaKsug2015 jpa5 스프링과jpa
Ksug2015 jpa5 스프링과jpa
 
SpringDataJPA - 스프링 캠프
SpringDataJPA - 스프링 캠프SpringDataJPA - 스프링 캠프
SpringDataJPA - 스프링 캠프
 
Ksug2015 - JPA2, JPA 기초와매핑
Ksug2015 - JPA2, JPA 기초와매핑Ksug2015 - JPA2, JPA 기초와매핑
Ksug2015 - JPA2, JPA 기초와매핑
 
Java class
Java classJava class
Java class
 
Java lambda
Java lambdaJava lambda
Java lambda
 
Hibernate start (하이버네이트 시작하기)
Hibernate start (하이버네이트 시작하기)Hibernate start (하이버네이트 시작하기)
Hibernate start (하이버네이트 시작하기)
 
Ksug2015 jpa4 객체지향쿼리
Ksug2015 jpa4 객체지향쿼리Ksug2015 jpa4 객체지향쿼리
Ksug2015 jpa4 객체지향쿼리
 
Spring data jpa
Spring data jpaSpring data jpa
Spring data jpa
 
자바야 놀자 PPT
자바야 놀자 PPT자바야 놀자 PPT
자바야 놀자 PPT
 
프론트엔드스터디 E03 - Javascript intro.
프론트엔드스터디 E03 - Javascript intro.프론트엔드스터디 E03 - Javascript intro.
프론트엔드스터디 E03 - Javascript intro.
 
영속성 컨텍스트로 보는 JPA
영속성 컨텍스트로 보는 JPA영속성 컨텍스트로 보는 JPA
영속성 컨텍스트로 보는 JPA
 
Java extends
Java extendsJava extends
Java extends
 
3-1. css
3-1. css3-1. css
3-1. css
 
Java 기초
Java 기초Java 기초
Java 기초
 
좌충우돌 ORM 개발기 | Devon 2012
좌충우돌 ORM 개발기 | Devon 2012좌충우돌 ORM 개발기 | Devon 2012
좌충우돌 ORM 개발기 | Devon 2012
 

En vedette

소프트웨어 학습 및 자바 웹 개발자 학습 로드맵
소프트웨어 학습 및 자바 웹 개발자 학습 로드맵소프트웨어 학습 및 자바 웹 개발자 학습 로드맵
소프트웨어 학습 및 자바 웹 개발자 학습 로드맵Javajigi Jaesung
 
어느 40대 아저씨 이야기
어느 40대 아저씨 이야기어느 40대 아저씨 이야기
어느 40대 아저씨 이야기Javajigi Jaesung
 
함수형 프로그래밍 언어 스칼라(Scala) 소개
함수형 프로그래밍 언어 스칼라(Scala) 소개함수형 프로그래밍 언어 스칼라(Scala) 소개
함수형 프로그래밍 언어 스칼라(Scala) 소개DongHee Kim
 
패캠 네트워킹 데이 - 침묵으로 가르치기
패캠 네트워킹 데이 - 침묵으로 가르치기패캠 네트워킹 데이 - 침묵으로 가르치기
패캠 네트워킹 데이 - 침묵으로 가르치기Javajigi Jaesung
 
커뮤니티 활동과 스터디
커뮤니티 활동과 스터디커뮤니티 활동과 스터디
커뮤니티 활동과 스터디Javajigi Jaesung
 
Scala스터디 - 배열사용하기
Scala스터디 - 배열사용하기Scala스터디 - 배열사용하기
Scala스터디 - 배열사용하기창규 김
 
[A4]de view2012 scala-michinisougu
[A4]de view2012 scala-michinisougu[A4]de view2012 scala-michinisougu
[A4]de view2012 scala-michinisouguNAVER D2
 
Spring camp 발표자료
Spring camp 발표자료Spring camp 발표자료
Spring camp 발표자료수홍 이
 
Objective-C에서 Swift로 전향할 때 생각해 볼 거리들
Objective-C에서 Swift로 전향할 때 생각해 볼 거리들Objective-C에서 Swift로 전향할 때 생각해 볼 거리들
Objective-C에서 Swift로 전향할 때 생각해 볼 거리들SeongGyu Jo
 
2014년에 만든 나만의 이력서
2014년에 만든 나만의 이력서2014년에 만든 나만의 이력서
2014년에 만든 나만의 이력서Javajigi Jaesung
 
트위터 봇 개발 후기
트위터 봇 개발 후기트위터 봇 개발 후기
트위터 봇 개발 후기종빈 오
 
[IGC 2016] 아마존 구승모 - 게임 제작을 위한 Amazon의 편리한 도구들 (게임리프트와 럼버야드)
[IGC 2016] 아마존 구승모 - 게임 제작을 위한 Amazon의 편리한 도구들 (게임리프트와 럼버야드)[IGC 2016] 아마존 구승모 - 게임 제작을 위한 Amazon의 편리한 도구들 (게임리프트와 럼버야드)
[IGC 2016] 아마존 구승모 - 게임 제작을 위한 Amazon의 편리한 도구들 (게임리프트와 럼버야드)강 민우
 
스프링캠프 2016 발표 - Deep dive into spring boot autoconfiguration
스프링캠프 2016 발표 - Deep dive into spring boot autoconfiguration스프링캠프 2016 발표 - Deep dive into spring boot autoconfiguration
스프링캠프 2016 발표 - Deep dive into spring boot autoconfiguration수홍 이
 
주니어 개발자의 개인프로젝트 개발기
주니어 개발자의 개인프로젝트 개발기주니어 개발자의 개인프로젝트 개발기
주니어 개발자의 개인프로젝트 개발기sung yong jung
 
Spark와 Hadoop, 완벽한 조합 (한국어)
Spark와 Hadoop, 완벽한 조합 (한국어)Spark와 Hadoop, 완벽한 조합 (한국어)
Spark와 Hadoop, 완벽한 조합 (한국어)Teddy Choi
 
Tdd live spring camp 2013
Tdd live spring camp 2013Tdd live spring camp 2013
Tdd live spring camp 2013beom kyun choi
 
[Spring Camp 2013] Java Configuration 없인 못살아!
[Spring Camp 2013] Java Configuration 없인 못살아![Spring Camp 2013] Java Configuration 없인 못살아!
[Spring Camp 2013] Java Configuration 없인 못살아!Arawn Park
 
Scala dreaded underscore
Scala dreaded underscoreScala dreaded underscore
Scala dreaded underscoreRUDDER
 
AWS의 하둡 관련 서비스 - EMR/S3
AWS의 하둡 관련 서비스 - EMR/S3AWS의 하둡 관련 서비스 - EMR/S3
AWS의 하둡 관련 서비스 - EMR/S3Keeyong Han
 

En vedette (20)

소프트웨어 학습 및 자바 웹 개발자 학습 로드맵
소프트웨어 학습 및 자바 웹 개발자 학습 로드맵소프트웨어 학습 및 자바 웹 개발자 학습 로드맵
소프트웨어 학습 및 자바 웹 개발자 학습 로드맵
 
어느 40대 아저씨 이야기
어느 40대 아저씨 이야기어느 40대 아저씨 이야기
어느 40대 아저씨 이야기
 
함수형 프로그래밍 언어 스칼라(Scala) 소개
함수형 프로그래밍 언어 스칼라(Scala) 소개함수형 프로그래밍 언어 스칼라(Scala) 소개
함수형 프로그래밍 언어 스칼라(Scala) 소개
 
패캠 네트워킹 데이 - 침묵으로 가르치기
패캠 네트워킹 데이 - 침묵으로 가르치기패캠 네트워킹 데이 - 침묵으로 가르치기
패캠 네트워킹 데이 - 침묵으로 가르치기
 
커뮤니티 활동과 스터디
커뮤니티 활동과 스터디커뮤니티 활동과 스터디
커뮤니티 활동과 스터디
 
Scala스터디 - 배열사용하기
Scala스터디 - 배열사용하기Scala스터디 - 배열사용하기
Scala스터디 - 배열사용하기
 
[A4]de view2012 scala-michinisougu
[A4]de view2012 scala-michinisougu[A4]de view2012 scala-michinisougu
[A4]de view2012 scala-michinisougu
 
Spring camp 발표자료
Spring camp 발표자료Spring camp 발표자료
Spring camp 발표자료
 
Objective-C에서 Swift로 전향할 때 생각해 볼 거리들
Objective-C에서 Swift로 전향할 때 생각해 볼 거리들Objective-C에서 Swift로 전향할 때 생각해 볼 거리들
Objective-C에서 Swift로 전향할 때 생각해 볼 거리들
 
2014년에 만든 나만의 이력서
2014년에 만든 나만의 이력서2014년에 만든 나만의 이력서
2014년에 만든 나만의 이력서
 
트위터 봇 개발 후기
트위터 봇 개발 후기트위터 봇 개발 후기
트위터 봇 개발 후기
 
[IGC 2016] 아마존 구승모 - 게임 제작을 위한 Amazon의 편리한 도구들 (게임리프트와 럼버야드)
[IGC 2016] 아마존 구승모 - 게임 제작을 위한 Amazon의 편리한 도구들 (게임리프트와 럼버야드)[IGC 2016] 아마존 구승모 - 게임 제작을 위한 Amazon의 편리한 도구들 (게임리프트와 럼버야드)
[IGC 2016] 아마존 구승모 - 게임 제작을 위한 Amazon의 편리한 도구들 (게임리프트와 럼버야드)
 
스프링캠프 2016 발표 - Deep dive into spring boot autoconfiguration
스프링캠프 2016 발표 - Deep dive into spring boot autoconfiguration스프링캠프 2016 발표 - Deep dive into spring boot autoconfiguration
스프링캠프 2016 발표 - Deep dive into spring boot autoconfiguration
 
주니어 개발자의 개인프로젝트 개발기
주니어 개발자의 개인프로젝트 개발기주니어 개발자의 개인프로젝트 개발기
주니어 개발자의 개인프로젝트 개발기
 
Spark와 Hadoop, 완벽한 조합 (한국어)
Spark와 Hadoop, 완벽한 조합 (한국어)Spark와 Hadoop, 완벽한 조합 (한국어)
Spark와 Hadoop, 완벽한 조합 (한국어)
 
Tdd live spring camp 2013
Tdd live spring camp 2013Tdd live spring camp 2013
Tdd live spring camp 2013
 
[Spring Camp 2013] Java Configuration 없인 못살아!
[Spring Camp 2013] Java Configuration 없인 못살아![Spring Camp 2013] Java Configuration 없인 못살아!
[Spring Camp 2013] Java Configuration 없인 못살아!
 
Scala dreaded underscore
Scala dreaded underscoreScala dreaded underscore
Scala dreaded underscore
 
Spring Boot 소개
Spring Boot 소개Spring Boot 소개
Spring Boot 소개
 
AWS의 하둡 관련 서비스 - EMR/S3
AWS의 하둡 관련 서비스 - EMR/S3AWS의 하둡 관련 서비스 - EMR/S3
AWS의 하둡 관련 서비스 - EMR/S3
 

Similaire à SLiPP 서비스를 Java에서 Scala로 전환하면서 경험담

자바 웹 개발 시작하기 (9주차 : 프로젝트 구현 – 추가적인 뷰)
자바 웹 개발 시작하기 (9주차 : 프로젝트 구현 – 추가적인 뷰)자바 웹 개발 시작하기 (9주차 : 프로젝트 구현 – 추가적인 뷰)
자바 웹 개발 시작하기 (9주차 : 프로젝트 구현 – 추가적인 뷰)DK Lee
 
Scala, Scalability
Scala, ScalabilityScala, Scalability
Scala, ScalabilityDongwook Lee
 
Java collections framework
Java collections frameworkJava collections framework
Java collections framework경주 전
 
From MSSQL to MySQL
From MSSQL to MySQLFrom MSSQL to MySQL
From MSSQL to MySQLI Goo Lee
 
JavaInAction 자바 8
JavaInAction 자바 8JavaInAction 자바 8
JavaInAction 자바 8NBT Inc.
 
Programming java day2
Programming java day2Programming java day2
Programming java day2Jaehoonyam
 
[세미나] 20160819 Java 프로그래머를 위한 Scala 튜토리얼
[세미나] 20160819 Java 프로그래머를 위한 Scala 튜토리얼[세미나] 20160819 Java 프로그래머를 위한 Scala 튜토리얼
[세미나] 20160819 Java 프로그래머를 위한 Scala 튜토리얼Sanghoon Yoon
 
R.java가 사라졌어요 어떻하죠?:Aquery라이브러리와 안드로이드 개발팁
R.java가 사라졌어요 어떻하죠?:Aquery라이브러리와 안드로이드 개발팁R.java가 사라졌어요 어떻하죠?:Aquery라이브러리와 안드로이드 개발팁
R.java가 사라졌어요 어떻하죠?:Aquery라이브러리와 안드로이드 개발팁창규 김
 
GraphQL overview
GraphQL overviewGraphQL overview
GraphQL overview기동 이
 
안드로이드 빌드: 설탕없는 세계
안드로이드 빌드: 설탕없는 세계안드로이드 빌드: 설탕없는 세계
안드로이드 빌드: 설탕없는 세계Leonardo YongUk Kim
 
0.javascript기본(~3일차내)
0.javascript기본(~3일차내)0.javascript기본(~3일차내)
0.javascript기본(~3일차내)Sung-hoon Ma
 
Catalyst Framework 살펴보기
Catalyst Framework 살펴보기Catalyst Framework 살펴보기
Catalyst Framework 살펴보기corund
 
TDD.JUnit.조금더.알기
TDD.JUnit.조금더.알기TDD.JUnit.조금더.알기
TDD.JUnit.조금더.알기Wonchang Song
 
Java8 & Lambda
Java8 & LambdaJava8 & Lambda
Java8 & Lambda기현 황
 
Spring boot 공작소(1-4장)
Spring boot 공작소(1-4장)Spring boot 공작소(1-4장)
Spring boot 공작소(1-4장)Choonghyun Yang
 
스위프트 성능 이해하기
스위프트 성능 이해하기스위프트 성능 이해하기
스위프트 성능 이해하기Yongha Yoo
 

Similaire à SLiPP 서비스를 Java에서 Scala로 전환하면서 경험담 (20)

자바 웹 개발 시작하기 (9주차 : 프로젝트 구현 – 추가적인 뷰)
자바 웹 개발 시작하기 (9주차 : 프로젝트 구현 – 추가적인 뷰)자바 웹 개발 시작하기 (9주차 : 프로젝트 구현 – 추가적인 뷰)
자바 웹 개발 시작하기 (9주차 : 프로젝트 구현 – 추가적인 뷰)
 
Scalability
ScalabilityScalability
Scalability
 
Scala, Scalability
Scala, ScalabilityScala, Scalability
Scala, Scalability
 
Java collections framework
Java collections frameworkJava collections framework
Java collections framework
 
4-1. javascript
4-1. javascript4-1. javascript
4-1. javascript
 
Scala
ScalaScala
Scala
 
From MSSQL to MySQL
From MSSQL to MySQLFrom MSSQL to MySQL
From MSSQL to MySQL
 
JavaInAction 자바 8
JavaInAction 자바 8JavaInAction 자바 8
JavaInAction 자바 8
 
S66 goos-w7
S66 goos-w7S66 goos-w7
S66 goos-w7
 
Programming java day2
Programming java day2Programming java day2
Programming java day2
 
[세미나] 20160819 Java 프로그래머를 위한 Scala 튜토리얼
[세미나] 20160819 Java 프로그래머를 위한 Scala 튜토리얼[세미나] 20160819 Java 프로그래머를 위한 Scala 튜토리얼
[세미나] 20160819 Java 프로그래머를 위한 Scala 튜토리얼
 
R.java가 사라졌어요 어떻하죠?:Aquery라이브러리와 안드로이드 개발팁
R.java가 사라졌어요 어떻하죠?:Aquery라이브러리와 안드로이드 개발팁R.java가 사라졌어요 어떻하죠?:Aquery라이브러리와 안드로이드 개발팁
R.java가 사라졌어요 어떻하죠?:Aquery라이브러리와 안드로이드 개발팁
 
GraphQL overview
GraphQL overviewGraphQL overview
GraphQL overview
 
안드로이드 빌드: 설탕없는 세계
안드로이드 빌드: 설탕없는 세계안드로이드 빌드: 설탕없는 세계
안드로이드 빌드: 설탕없는 세계
 
0.javascript기본(~3일차내)
0.javascript기본(~3일차내)0.javascript기본(~3일차내)
0.javascript기본(~3일차내)
 
Catalyst Framework 살펴보기
Catalyst Framework 살펴보기Catalyst Framework 살펴보기
Catalyst Framework 살펴보기
 
TDD.JUnit.조금더.알기
TDD.JUnit.조금더.알기TDD.JUnit.조금더.알기
TDD.JUnit.조금더.알기
 
Java8 & Lambda
Java8 & LambdaJava8 & Lambda
Java8 & Lambda
 
Spring boot 공작소(1-4장)
Spring boot 공작소(1-4장)Spring boot 공작소(1-4장)
Spring boot 공작소(1-4장)
 
스위프트 성능 이해하기
스위프트 성능 이해하기스위프트 성능 이해하기
스위프트 성능 이해하기
 

SLiPP 서비스를 Java에서 Scala로 전환하면서 경험담

  • 2. Scala를 선택하기까지의 삽질 경험담 Spring + JPA + Scala 조합을 선택한 이유 SLiPP 서비스를 Java에서 Scala로 전환하는 과정 Scala 전환을 통해 얻는 이점 Spring + JPA + Scala 조합시 고려할 사항
  • 4. 40대를 넘어선 2013년의 어느 날 • 10년 이상 Java만 했더니 재미없다. • 최근에 함수형 언어가 대세라더라. • 삶이 무료하다. 뭔가 재미있는 일 좀 있으면 좋겠다. • 40대가 되면 생긴다는 그 병
  • 5. 일단 시작하는 거 제대로 공부하자.
  • 6. • Scheme이라는 완전히 생소한 언어에 도전 • 내용 진짜 어려움 • 실무에 적용할 곳이 보이지 않음. • 서서히 동기부여가 되지 않으면서 중도 포기.
  • 7. • Scheme이라는 완전히 생소한 언어에 도전 • 내용 진짜 어려움 • 실무에 적용할 곳이 보이지 않음. • 서서히 동기부여가 되지 않으면서 중도 포기. 하지만 함수형 언어가 무엇인지에 대한 대략적인 감은 잡음
  • 8. 2013년 말 다시 병이 도짐 • 최근에 함수형 언어가 대세라더라. • 삶이 무료하다. 뭔가 재미있는 일 좀 있으면 좋겠다.
  • 9. • JVM 기반 함수형 언어를 학습하면 실무 적용 가능할거야. • Clojure는 Scheme과 문법이 비슷하잖아. • SLiPP 스터디에 Clojure 스터디 진행
  • 10. • 스터디가 항상 그렇듯이 초반 3주차까지 좋았음. • 4주차 이후부터 스터디원이 거의 나오지 않음. • 스터디 중도 포기. 지금까지 여러 번의 SLiPP 스터디를 진행했지만 중도 포기한 유일한 스터디 • 나도 실무에 적용할 방법을 찾지 못함
  • 11. 하지만 스터디원 중의 한 명은 클로저 빠로 만듦
  • 12. 2014년 ~ 2015년 초반 • 마흔이 넘어 무슨 함수형 언어야. • 그냥 자바로 15년만 더 버티자. • 그러던 어느 날 스터디에 Scala를 하잖다.
  • 13. • 함수형의 극한을 맛보는 경험을 포기하고 현실과 타협하자. • 나란 인간은 실무에 적용하지 않으면 지속적인 동기부여를 하기 힘들다. • 욕심을 버리고 Scala로 가자. • 스스로 자위하면서 스터디에 합류.
  • 14. • 아직도 함수형 프로그래밍에 대한 욕심을 버리지 못함. • 책 무지 좋다. 단, Scala 문법도 익숙하지 않은 초보자가 보기 너무 어려움. • 스터디 초반 교재로 고민하다 포기함.
  • 15. • 돌고 돌아 함수형 프로그래밍에 대한 욕심을 버리고 Programming in Scala • 욕심을 버렸는데 재미가 없다. Scala도 스터디 끝나면 기억 속에서 사라질 거 같다. • SLiPP을 Java에서 Scala로 전면 개편하자.
  • 16. 느낀 점 • 2013년 무작정 시작했던 함수형 프로그래밍에 대한 도전이 Scala를 통해 결실을 맺다. • 너무 많은 것을 고려한 후 실행에 옮기기 보다 무작정 실행해 보는 것도 삶에 새로운 길을 만든다.
  • 17. Spring + JPA + Scala 조합을 선택
  • 18. • 돌고 돌아 함수형 프로그래밍에 대한 욕심을 버리고 Programming in Scala • 욕심을 버렸는데 재미가 없다. Scala도 스터디 끝나면 기억 속에서 사라질 거 같다. • SLiPP을 Java에서 Scala로 전면 개편하자.
  • 19. • 돌고 돌아 함수형 프로그래밍에 대한 욕심을 버리고 Programming in Scala • 욕심을 버렸는데 재미가 없다. Scala도 스터디 끝나면 기억 속에서 사라질 거 같다. • SLiPP을 Java에서 Scala로 전면 개편하자.
  • 20.
  • 21. Play가 아닌 Scala + Spring + JPA 조합을 선택한 이유 • SLiPP 서비스를 처음부터 다시 구현하는 것에 대한 부담감 • Spring과 JPA 경험을 버리는 것의 아쉬움. • 중도 포기하지 않으려면 변화를 Scala 하나로 한정.
  • 27. Scala 적용(리팩토링) 원칙 • 기존 기능을 서비스하면서 점진적으로 리팩토링한다. • 리팩토링 단계에 컴파일 에러가 발생하는 시간을 최소화한다.
  • 28. 1단계 – Java와 Scala 동시 개발 환경 구축
  • 29. • Java와 Scala 코드가 동시에 컴파일과 실행이 가능하도록 환경 구축함. • Java 소스 코드 • src/main/java, src/test/java • Scala 소스 코드 • src/main/scala, src/test/scala
  • 32. • 10년 이상 이클립스 기반으로 살아왔다. • 이클립스에서 Scala IDE 활용해 Scala 개발 시작하는 것은 당연한 수순이었다. • 이클립스 버리고 싶지 않았는데 몇 달 후 GG
  • 33. • 이클립스 버리고 싶지 않았다. • 하지만 Scala는 Intellij의 Scala 플러그인이 갑이다. • Community 버전에서도 무료로 사용 가능하다.
  • 34. 2단계 – 위험 요소가 높은 부분부터 실험
  • 35. • Java 기반의 SLiPP 코드는 Spring + JPA(Spring Data JPA) 구조 • JPA 기반 Entity를 Scala로 개발 가능한지 실험 • 다른 Entity와 가장 의존관계가 적은 기능을 Scala로 먼저 변경 • JPA Entity 실험과 리팩토링 원칙에 대한 경험을 같이 진행
  • 36. package net.slipp.domain.tag; [...] @Entity @Table(indexes = { @Index(name = "idx_tagged_history_tag", columnList="tag_id"), @Index(name = "idx_tagged_history_question", columnList="question_id")} ) @EntityListeners({ CreatedDateEntityListener.class }) public class TaggedHistory implements HasCreatedDate { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long historyId; @Column(name = "tag_id", nullable = false, updatable = false) private Long tagId; @Enumerated(EnumType.STRING) @Column(name = "tagged_type", nullable = false, updatable = false, columnDefinition = TaggedType.COLUMN_DEFINITION) private TaggedType taggedType; public TaggedHistory(Long tagId, Long questionId, Long userId, TaggedType taggedType) { this.tagId = tagId; this.questionId = questionId; this.userId = userId; this.taggedType = taggedType; } [...] }
  • 37. Scala 적용(리팩토링) 원칙 • 기존 기능을 서비스하면서 점진적으로 리팩토링한다. • 리팩토링 단계에 컴파일 에러가 발생하는 시간을 최소화한다.
  • 38. 자바 리팩토링 과정과 같은 방식으로 Java => Scala 전환 과정 설계
  • 39. public abstract class JdbcTemplate { public void update() throws SQLException { […] } public abstract String createQuery(); public abstract void setValues(PreparedStatement pstmt) throws SQLException; } 리팩토링 전
  • 40. public abstract class JdbcTemplate { public void update() throws SQLException { […] } public void update(String sql) throws SQLException { […] } public abstract String createQuery(); public abstract void setValues(PreparedStatement pstmt) throws SQLException; } 리팩토링 중(과도기 단계)
  • 41. public abstract class JdbcTemplate { public void update(String sql) throws SQLException { […] } public abstract void setValues(PreparedStatement pstmt) throws SQLException; } 리팩토링 후
  • 42. Scala 적용(리팩토링) 원칙 • 기존 기능을 서비스하면서 점진적으로 리팩토링한다. • 리팩토링 단계에 컴파일 에러가 발생하는 시간을 최소화한다. • 리팩토링 전과 후의 코드가 공존하는 단계가 반드시 필요하다. • 이 같은 전략은 소스 코드 리팩토링 뿐 아니라 Java => Scala 전환, DB 리팩토링 또한 같다. • 리팩토링 전과 후의 결과를 쉽게 테스트할 수 있어야 한다.
  • 43. Java => Scala 전환 과정 설계(예, TaggedHistory) • TaggedHistory.java => NTaggedHistory.scala • Scala로 변경한 코드에서 생성된 Table Schema와 기존 자바 코드에서 생성된 Table Schema가 같은지 검증한다. Table Schema가 같은 시점을 Entity 변환 완료 시점으로 가정한다. • Table Schema를 검증하기 위한 테스트 도구가 필요. • TaggedHistory를 사용하는 코드를 NTaggedHistory를 사용하도록 변경한다. • 테스트한다. • TaggedHistory Entity를 삭제한다. • NTaggedHistory.scala => TaggedHistory.scala로 rename
  • 44. package net.slipp.ndomain.tag [...] @Entity(name="TaggedHistory") @Table(indexes = Array( new Index(name = "idx_tagged_history_tag", columnList="tag_id"), new Index(name = "idx_tagged_history_question", columnList="question_id"))) class NTaggedHistory(t: Long, q: Long, u: Long, tType: String) extends DomainModel with NHasCreatedDate { @Id @GeneratedValue(strategy = GenerationType.AUTO) var historyId: Long = _ @Column(name = "tag_id", nullable = false, updatable = false) val tagId = t @Column(name = "tagged_type", nullable = false, updatable = false, columnDefinition = NTaggedType.ColumnDefinition) val taggedType = tType def this() = this(0L, 0L, 0L, null) }
  • 45. 매핑 중 삽질 내용 1 • Scala Annotation에서 배열을 사용하는 경우 기존 Java Annotation 배열({})을 사용할 수 없었다. Scala Annotation 배열은 Array package net.slipp.ndomain.tag [...] @Entity(name="TaggedHistory") @Table(indexes = Array( new Index(name = "idx_tagged_history_tag", columnList="tag_id"), new Index(name = "idx_tagged_history_question", columnList="question_id"))) class NTaggedHistory(t: Long, q: Long, u: Long, tType: String) extends DomainModel with NHasCreatedDate { @Id @GeneratedValue(strategy = GenerationType.AUTO) var historyId: Long = _ @Column(name = "tag_id", nullable = false, updatable = false) val tagId = t @Column(name = "tagged_type", nullable = false, updatable = false, columnDefinition = NTaggedType.ColumnDefinition) val taggedType = tType def this() = this(0L, 0L, 0L, null) }
  • 46. 매핑 중 삽질 내용 2 • 매핑 과정 중 해결하지 못한 문제는 java enum을 활용해 Mapping하는 부분이다. Scala에서도 Enumeration을 사용하는 Enum이 있지만 java의 enum과는 동작방식이 달라 그대로 사용할 수 없다. • 1차 해결 방법은 Scala Entity에서 java enum을 사용하도록 한다. Scala 코드와 Java 코드가 섞여서 사용되는 구조가 된다. • 2차 해결 방법은 Scala Enum 또는 Scala Case Object를 활용해 해결해야 한다. 이 경우 Entity를 사용하는 Java 코드에서 Scala 클래스에 접근하지 못하는 이슈와 Entity 내부에서 매핑을 위한 변환 작업이 필요하다.
  • 47. 실험을 통한 결론 및 얻게 된 경험 • Scala 기반으로 JPA 기반 개발 가능. • Scala => Java API 접근은 문제 없음. But, Java => Scala API 접근에 한계가 많음을 느낌. • Controller => Service => Repository + Entity 순서로 리팩토링 전략 수립함.
  • 48. 3단계 – Layer별로 Scala 전환
  • 49. • Controller부터 Scala로 리팩토링 시작 • 리팩토링 과정 • src/main/scala의 같은 package에 NHomeController.scala를 생성 • NHomeController로 URL 하나씩 이동하면서 관련된 method 이전함. • 컴파일 에러가 없는 상태로 만든 후 HomeController.java에서 Controller Annotation 제거함. • 웹 서버 시작해 기능이 정상적으로 동작하는지 테스트 • 정상 동작을 확인하면 HomeController.java를 제거함. • NHomeController 파일을 HomeController로 rename 리팩토링 진행함. • 모든 Controller에 대해 무한 반복
  • 50. 지루하고 짜증나는 반복 과정의 연속 속도가 잘 나지 않는다.
  • 51. 방법 1. IntelliJ의 신세계 경험 Java 소스 코드를 Scala 소스 코드로 자동 변환해 준다.
  • 52. 방법 2. 일단 모든 Java 코드를 Scala 코드로 전환하는 것을 1차 목표로 한다. Scala 코드로 전환하는 과정에서 절대 리팩토링을 하지 않는다.
  • 53. 토할 정도로 Java 코드를 Scala 코드로 전환하는 것에만 집중.
  • 54.
  • 56. private String sendMessageToFacebook(SocialUser loginUser, String link, String receiverId, String message) { String postId = null; try { FacebookClient facebookClient = createFacebookClient(loginUser); int i = 0; do { if (i > 2) { break; } FacebookType response = facebookClient.publish(receiverId + "/feed", FacebookType.class, Parameter.with("link", link), Parameter.with("message", message)); postId = response.getId(); i++; } while (postId == null); log.debug("connect post id : {}", postId); } catch (Throwable e) { log.error("Facebook Connection Failed : {}", e.getMessage()); } return postId; } 리팩토링 사례 1 – java 코드
  • 57. @annotation.tailrec private def retry[T](n: Int)(fn: => T): T = { Try { fn } match { case Success(x) => x case _ if n > 1 => retry(n - 1)(fn) case Failure(e) => throw e } } 리팩토링 사례 1 – scala 코드 private def sendMessageToFacebook(loginUser: SocialUser, link: String, receiverId: String, message: String) = { retry(3) { val facebookClient: FacebookClient = createFacebookClient(loginUser) val response: FacebookType = facebookClient.publish(receiverId + "/feed", classOf[FacebookType], Parameter.`with`("link", link), Parameter.`with`("message", message)) response.getId } }
  • 58. public Set<Tag> processGroupTags(Set<FacebookGroup> groupTags) { Set<Tag> tags = Sets.newHashSet(); for (FacebookGroup each : groupTags) { if (each.isEmpty()) { continue; } Tag tag = tagRepository.findByGroupId(each.getGroupId()); if (tag != null) { tags.add(tag); continue; } tag = tagRepository.findByName(each.getName()); if (tag != null) { tag.moveGroupTag(each.getGroupId()); tags.add(tag); continue; } Tag newTag = Tag.groupedTag(each.getName(), each.getGroupId()); tags.add(tagRepository.save(newTag)); } return tags; } 리팩토링 사례 2 – java 코드
  • 61. Java에서 Scala 전환 단계 • 1단계 : Java와 Scala를 같이 실행할 수 있는 환경을 구축한다. • 2단계 : Scala 전환시 위험요소가 있다고 판단되는 부분이 있다면 이에 대한 실험을 먼저 진행하고 전략을 세운다. • 3단계 : 앞에서 세운 전략에 따라 Scala 전환 작업을 진행한다. • 4단계 : Scala 스타일로 리팩토링한다.
  • 63. 1. Domain과 DTO의 명확한 분리에 대한 거부감이 줄어듦 • 현재 개발 추세는 Domain 객체와 DTO에 중복되는 부분이 많아 자바 객체 하나가 Domain 역할, DTO 역할을 하는 방식으로 구현. • Scala를 활용하면 각 역할별로 구현하는 것에 대한 거부감이 줄어듦
  • 64. @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를 생성하지 않아도 된다.
  • 65. @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 추가함.
  • 66. 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 자동 추가
  • 67. Domain과 DTO의 명확한 분리에 대한 거부감이 줄어듦 • 분리하는 것이 항상 좋은 것은 아니다. • 상황에 따라 Domain과 DTO를 분리/통합할 것인지에 대한 역량을 키우는 것이 더 중요하다.
  • 68. 2. Test Fixture(Test Data) 생성하기 용이함. • 자바에서 Test Fixture를 생성하고 변경하기 어려움은 Test 코드를 만드는데 약간의 장애물이다. • Scala는 named parameter를 통해 해결 가능
  • 69. email, nickname, password를 가지는 User 객체에 대한 테스트를 데이터를 만든다면…
  • 70. 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); } }
  • 71. 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장. 복잡한 테스트 데이터 만들기 참고
  • 72. 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")
  • 73. • Case Class • Named Parameter • Implicit • Some/Option
  • 74. • Lambda(람다) • 막강 Collection • 모나드 등등 … • Functional Programming • Case Class • Named Parameter • Implicit • Some/Option
  • 75. 3. 삶의 무료함을 치유 • 끊임없는 리팩토링 경험 • 나름 생산성 높음 • 객체 지향과 함수형 프로그래밍의 절묘한 조화 • 시대에 뒤쳐지지 않는 듯한 나만의 착각
  • 76. 4. 덤으로 swift 학습 • Scala를 사용했더니 swift 프로그래밍이 껌이다.
  • 77. Scala 및 Scala + Spring + JPA 기반으로 개발할 때 고려할 부분
  • 78. Scala + Spring + JPA 조합에서 불편한 부분 • Java 기반 프레임워크 사용하면서 고려할 부분이 생긴다. • Scala의 모든 기능을 극한으로 사용하는데 제약 사항이 있다.
  • 79. @RestController class UserController @Autowired() (val userRepository: UserRepository) { val Log = LoggerFactory.getLogger(classOf[UserController]) @RequestMapping(value = Array("/users"), method = Array(RequestMethod.POST) ) def join(@Valid @RequestBody userForm: UserForm, result: BindingResult) = { ... } @RequestMapping(value = Array("/users/{userEmail}"), method = Array(RequestMethod.POST) ) def login(@PathVariable userEmail: String, @RequestParam(required = true) password:String) = { ... } }
  • 80. import java.lang.Long import org.springframework.data.repository.CrudRepository trait UserRepository extends CrudRepository[User, Long] { def findByEmail(email: String): User def findByEmailAndPassword(email: String, password: String): User def findByNickName(nickName: String): User }
  • 81. import java.util.{ArrayList, List} @Entity class Clan(pName: String) extends DomainModel { ... @OneToMany(mappedBy = "clan", cascade = Array(CascadeType.PERSIST, CascadeType.REMOVE), fetch = FetchType.LAZY) val clanMembers: List[ClanMember] = new ArrayList[ClanMember] }
  • 82. Scala 컴파일 시간 • Scala의 가장 큰 단점은 컴파일 시간이 많이 소요된다. • Scala 기반으로 개발하려면 SSD는 필수. 장비빨이 받쳐주어야 한다.
  • 83. Scala 학습 비용이 높다. • 한 번에 Scala의 모든 기능을 활용하려고 하기 때문이다.
  • 84. Scala 적용 단계 1. Scala를 자바처럼 구현한다. 2. 점차 Scala 문법에 친숙해지면 Scala 기능을 하나씩 적용한다. 3. 함수형 프로그래밍 스타일로 구현한다.
  • 86. Java만 너무 오래 했더니 재미없다. 새로운 언어에 도전해 보고 싶다. 그런데 그 동안 쌓아온 경험을 버리기는 아깝다. Scala가 대안이 될 수 있다.
  • 87. 최근 함수형 프로그래밍이 뜬다더라. 새로운 언어를 도입해 함수형 프로그래밍을 학습하고 싶지만 현재 운영 중인 시스템이 Java 환경이다. Scala가 대안이 될 수 있다.
  • 88. 프로젝트를 새로 시작한다면 play framework도 대안
  • 89. 대부분의 개발자가 Scala에 익숙하지 않고, Spring 프레임워크에 친숙하다면 Scala + Spring 조합도 괜찮은 조합이 대안이 될 수 있다.
  • 90. 자바 프로그래머로 15년 이상 살아온 나에게는 2016년 현재 Spring-Boot, JPA, Scala가 최선의 조합이고, 흥미로운 조합이라 생각
  • 91. 하지만 Swift로 백엔드 프로그래밍이 가능하다면 1년 후에는 자바 생태계를 떠날지도…

Notes de l'éditeur

  1. 50
  2. 51
  3. 52
  4. 53
  5. 54
  6. 56
  7. 57
  8. 58
  9. 59