현재 slipp.net 프로젝트를 java 소스 코드에서 scala 소스 코드로 변경하고 있다. 변경 과정에서 난감한 것 중의 하나는 Scala는 java에서 지원하는 break와 continue를 지원하지 않고 있다.
어제 리팩토링 중 정확하게 continue를 사용하는 코드가 등장했다. 경우에 따라 break, continue 대신 재귀 함수를 통해 해결할 수도 있는데 어제 발생한 코드는 재귀 함수로 해결하기 힘든 구조여서 고민 끝에 Option으로 해결해 봤다.
Scala로 변환하기 전의 Java 코드는 다음과 같다.
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;
}
while문을 속에서 DB에서 조회한 값이 존재하느냐에 따라 continue를 사용하는 일반적인 코드다. 위 코드를 Scala의 Option과 Collection의 map method를 사용해 다음과 같이 리팩토링했다.
private def getTagByFacebookGroup(facebookGroup: FacebookGroup) = {
Option(tagRepository.findByGroupId(facebookGroup.getGroupId))
.getOrElse(Option(tagRepository.findByName(facebookGroup.getName))
.getOrElse {
val newTag: Tag = Tag.groupedTag(facebookGroup.getName, facebookGroup.getGroupId)
tagRepository.save(newTag)
})
}
def processGroupTags(groupTags: Set[FacebookGroup]): Set[Tag] = {
groupTags.map(getTagByFacebookGroup)
}
위와 같이 리팩토링을 한 결과 코드를 보면 Option의 getOrElse를 이중으로 사용해 해결하고 있다. 이 코드가 가독성 측면에서 괜찮은지 모르겠다. 물론 Scala의 Option에 익숙하면 이전 코드보다 좀 더 직관적일 수 있다고 생각하지만..
이와 다른 방식의 접근이 있다면 조언해 주면 좋겠다. 이런 방식으로 이전 코드를 Scala로 변환하면서 Scala를 학습해 나가는 것도 나름 재미있다.
0개의 의견 from FB
11개의 의견 from SLiPP
orElse를 이용해서 중첩을 뺄 수 있겠네요.
옮기시는 과정에서
tag.moveGroupTag(each.getGroupId())
가 빠졌네요. map으로 처리하면 됩니다.자바로 이런 식으로 해봤는데 쓰기나 읽기나 더 좋은지는 모르겠어요. 작성도 어렵고 잠시 후에 다시 봐도 뭐하는 코드인지 한참 봐야하더라구요.
@eungju.park.1 thx. Option으로 옮기는 것에만 집중하다보니 중요한 로직을 빠트렸네.
@eungju.park.1 네 피드백을 반영해서 다음과 같이 수정했다.
orElse 써서 중첩 줄이니까 좋네. Option에서 바로 get 하면 어떻게 처리할지 막막할텐데 map 써서 처리 가능하니 좋네. scala의 막강함을 다시 한번 느끼네 되네
제가 볼때는 애초에
findXxxxx
메소드 자체가Option
타입을 반환하는게 더 좋을거 같습니다.find
메소드의 경우 찾는 데이터가 있을수도 있고 없을수도 있기 때문에null
을 반환할 가능성이 생기는데, Scala를 쓰시면 아예null
을 취급 안 하는 방식이 가장 좋습니다. 그래서 저에게 나머지 코드가 있는게 아니니 그냥 대충trait
로 뽑아보자면,이렇게 만들고 위의 코드는 이렇게 변경이 가능하겠죠.
저의 경우는 자바로 코딩할때도
Repository
의find
메소드들이Optional
타입을 반환하게 만듭니다. 물론 자바8을 사용하고 있으니 가능한건데 8을 못쓰면 Guava 같은걸 써서 해야겠죠.@자바지기 findByName 성공일 때만 moveGroupTag를 해야하니 map을 orElse 안으로 넣어야 기존 의미와 더 일치합니다.
이런 패턴이 자주 보이니 Optional[R]을 리턴하는 함수 목록을 받아서 차례로 실행하여 첫 Some을 돌려주는 함수를 만들면 좋겠네요.
@Kevin 저도 find 메서드에서 Option을 반환하는 방식이 좋다고 생각합니다. 그런데 Repository에 해당하는 놈들이 spring data jpa 기반의 프레임워크로 동작하고 있는데요. 이 부분을 Optional로 반환할 수 있을까요? 제 코드가 Spring 프레임워크 기반하에서 Scala를 사용하다보니 Option을 극대화해서 사용하는데는 한계가 있는 듯 해서요. 혹시 아이디어 주시면 도전해 볼께요.
@자바지기 아... Spring을 쓰고 계시군요. ㅡ_ㅡ; Scala를 사용하셔서 Spring을 쓰고 계실거란 생각을 못 했네요. 그렇다면 Spring측에서는 Scala를 제대로 사용하도록 만들지 못했단 거군요. :( 전 Java 쓸 경우도
GenericRepository
를 직접 만들어서 쓰고 있어서 (아마 스프링에 있는거랑 거의 비슷할겁니다), Spring Data를 쓰면서 이문제를 어떻게 쉽게 해결할 수 있을지 잘 모르겠습니다.GenericRepository
를 직접 만들어 쓰시는건 어떨까요? 사실 제 책(Scala가 아니라 Java8이지만)에 집어 넣을 내용이기도 합니다만, 쉽게 직접 만들어 쓰실 수 있을거 같습니다. 뭐 정 안되면 Wrapper 같은걸 만들어서 내부적으로는 Spring의 Repository를 사용하시는 방법도 있겠죠. 제가 Scala와 Spring을 같이 쓰는거에 대한 고민을 해본적이 없어서, 현재로서는 더 나은 대답이 생각나지 않는군요. 혹시 찾게 되시면 공유해 주시면 감사하겠습니다. :)음..점심시간에 잠깐 생각해본건데 컴파일은 안해봤습니다.. 요구 사항을 다음과 같다고 혼자 추측하고;;; // facebook group을 뒤지면서.. // groupId가 empty면 넘어가고.. // groupId가 있으면 group id로 tag를 찾아서 있으면 해당 tag 리스트에 추가.. // groupId가 있으면 group id로 tag를 찾았는데 tag가 없으면 이름으로 tag를 찾고 찾으면 리스트에 추가.. // 두가지 방법으로 찾았는데 관련 Tag 없으면 하나 만들어서 넣는다.
“repository의 "find계열의 함수의 return type이 Option이라면, save의 경우 save된 tag라면" 이라고 생각하고 작성해봤습니다. 저의 경우 보통 그렇게 사용하거든요..” => 요 부분은 글 쓰는 동안 댓글이 달렸으므로..무효..Option으로 처리해봤습니다..허허허..
제 기준으로 이해가 잘 될만한 naming 및 코드 레벨로 정리해본건데..컴파일도 안돌려본거라..ㅎㅎ 참고 부탁드려요^^;;
@자바지기 이거 한번 보세요. :) https://github.com/spring-projects/spring-data-examples/tree/master/jpa/java8
일단
Optional
로 받으신후에Optional
에서Option
으로의 변환은 scala-java8-compat의import scala.compat.java8.OptionConverters._
를 사용하시면 쉽게 될거 같습니다만, 직접 해보지 않아서 모르겠네요. ^^;근데 Spring에서
Optional
타입을 지원하기 시작했으면 Spring Scala에서도Option
을 지원해야 할거 같은데, 아직인가 보죠? 오히려 Spring Scala의 경우는 Java8지원 이전부터Option
을 지원했어야 정상으로 보입니다만...@Kevin 감사합니다. Optional 지원하고 있으니 Scala와도 어떤 식으로든 연계할 수 있을거 같네요. 한번 시도해 볼께요. spring data jpa가 된다면 spring mvc에서도 사용자가 입력한 값에 대해 Optional 처리가 될 수도 있겠네요. 그런 날이 오기를 기대해 봅니다.
의견을 남기기 위해서는 SLiPP 계정이 필요합니다.
안심하세요! 회원가입/로그인 후에도 작성하시던 내용은 안전하게 보존됩니다.
SLiPP 계정으로 로그인하세요.
또는, SNS 계정으로 로그인하세요.