DAO(Repository)에서 발생한 예외처리 질문

5,493 views
Skip to first unread message

이성현(kemuel)

unread,
Mar 17, 2012, 12:39:50 AM3/17/12
to Korea Spring User Group
DAO(Repository)에서 발생한 예외를 Service에서 처리해야 할 지 아니면 Controller에서 처리해야 할 지
의문입니다.

아래 코드는 저 나름대로 예외 처리를 한 코드 입니다.
MemberDAO에서 중복 예외를 발생 시켜 Controller에서 처리를 합니다.
DuplicateMemberException는 DuplicateKeyException을 의미 있는 uncheck
exception 으로 전환 하기 위해 만든 Exception입니다.
먼저 Member가 중복인지 아닌지 select로 검사 할 수 있지만 저는 중복을 예외를 처리하려고 했습니다.
즉, 중복 Member는 존재하지 않는다는 비즈니스 로직을 예외로 처리하는 것입니다.

궁금한 점은 controller에서 비지니스 로직적인 것을 controller에서 예외처리를 해야 하는지
MemberService에서 또 try catch문으로 예외를 잡아서 처리하여 의미 있는 예외를 controller로
throws 하거나
아니면 return value를 true/fase 로 변경해야 하는 건지 궁금합니다.

제 생각은 controller에서 예외처리를 하면 MemberService 쪽이 깔끔한(나이스)한 코드가 되어 좋아 보입니다.

다른 예외 전략(처리)가 있다면 답변 부탁 드립니다.

============== Controller 에서 예외처리 ====================
========== MemberDAO =============================
@Override
public void insertMember(Member member) throws
DuplicateMemberException {
try {
sqlMapClientTemplate.insert("member.insertMember", member);
} catch (DuplicateKeyException e) {
throw new DuplicateMemberException(e);
}
}

========== MemberService =============================
@Override
public void registMember(Member member) {
memberDAO.insertMember(member);
}

========== MemberController =============================
@RequestMapping("/member/registmember.do")
public String registMember(Member member) {
try {
memberService.registMember(member);
return "main"
} catch (DuplicateMemberException e) {
return "/member/viewregistmemberform";
}
}

============== service에서 예외처리 ====================
========== MemberService =============================
@Override
public boolean registMember(Member member) {
try {
memberDAO.insertMember(member);
return true;
} catch (DuplicateMemberException e) {
return false
}
}

========== MemberController =============================
@RequestMapping("/member/registmember.do")
public String registMember(Member member) {
if(memberService.registMember(member) {
return "main";
} else {
return "viewregistmemberform";
}
}


강동욱

unread,
Mar 17, 2012, 8:39:38 PM3/17/12
to ks...@googlegroups.com
개인적으로는 @Controller에 try … catch 문이 나오는 것이 옳지 않다고 생각합니다.

Validator를 이용하여 검증을 서비스 계층으로 이동시키더라도 컨트롤러에서 BindingResult를 이용해 에러 결과를 뽑아보는 방식이 에러처리에도 더욱 적절할 듯 싶네요.

<form:errors>같은 스프링 EL문법도 사용가능해지구요.

이성현(kemuel)

unread,
Mar 18, 2012, 1:21:57 AM3/18/12
to Korea Spring User Group
저두 try catch가 controller에 있는게 보기 좋지 않습니다.

Validation은 값(value)검증만 하는 것이라 생각했는데
Exception 처리도 하는거 군요.

view(jsp)와 controller 사이에 값 검증만 해봐서 확 와닿지 않아서 그런데
Validator로 Exception 처리하는 참조 사이트든가 처리하는 흐름만 좀 알려주 주시면 정말 감사 하겠습니다.

Sungchul Park

unread,
Mar 18, 2012, 10:30:00 PM3/18/12
to ks...@googlegroups.com

이 경우에는 컨터롤러에서 예외를 처리하는 쪽이 적절하다고 봅니다.

MemberDAO에서는 DB Access 관련 예외인 DuplicateKeyException를 도메인 예
외인 DuplicateMemberException로 잘 바꿔서 서비스에서 넘겼는데
DuplicateKeyException 자체가 이미 구현 기술 의존성을 숨긴 추상화한 된 예
외이나 도메인의 스프링 의존성도 줄인다는 관점에서는 의미 있는 작업 같습니다.

MemberService에서 메서드 시그니처에 throws DuplicateMemberException를 추
가해서 명문화했으면 좋았겠습니다.

MemberController의 try-catch는 프레임워크 차원에서도 처리할 수 있지만 공
통사항이 아니라 특정 업무의 워크플로에 해당하므로 컨트롤러에서 흐름을 제
어하는 편이 좋다고 생각합니다.

저는 왜 두 경우를 두고 고민하시는지 이해하지 못하겠네요. 서비스에서 처리
하면 어떤 이득이 있는 거죠? 코드량이 늘었을 뿐 아니라 서비스의 반환값이
어떤 의미인지 알려면 javadoc 같은 문서를 봐야만 분명해지는 번거로움까지
생겼는데요.

이상용

unread,
Mar 18, 2012, 11:36:16 PM3/18/12
to ks...@googlegroups.com
음.. 저는 약간 생각이 다릅니다.
개발자의 성향이나 전체 프레임워크 플로워에 따라서 다르겠지만

되도록이면 Controller에서는 Exception 처리를 하지 않는 편이 좋다고 봅니다.
제 경우에는 
View - Controller - Validation Logic- Business Logic - DAO

이런식으로 많이 프로그래밍을 합니다.

약간의 코드가 증가되기는 하지만 

전체적인 코드의 복잡도나 재활용면에서도 더 효율적으로 프로그래밍을 작성할 수 있는것 같습니다.
(물론 Class의 양이 많이 늘어나기는 하지만^^;;)


2012년 3월 19일 오전 11:30, Sungchul Park <gyu...@gmail.com>님의 말:


--
Google 그룹스 'Korea Spring User Group' 그룹에 가입했으므로 본 메일이 전송되었습니다.
이 그룹에 게시하려면 ks...@googlegroups.com(으)로 이메일을 보내세요.
그룹에서 탈퇴하려면 ksug+unsubscribe@googlegroups.com로 이메일을 보내주세요.
더 많은 옵션을 보려면 http://groups.google.com/group/ksug?hl=ko에서 그룹을 방문하세요.


Sungchul Park

unread,
Mar 19, 2012, 12:11:38 AM3/19/12
to ks...@googlegroups.com

> 되도록이면 Controller에서는 Exception 처리를 하지 않는 편이 좋다고 봅니다.
어떤 이유인지 궁금하네요.
관련 자료라도 알려주시면 고맙겠습니다.

> 제 경우에는
> View - Controller - Validation Logic- Business Logic - DAO
>

Validation Logic이 하는 역할은 무엇인가요?

이상용

unread,
Mar 19, 2012, 12:21:57 AM3/19/12
to ks...@googlegroups.com
조금 더 자세히 설명을 드리자면..
Validation Logic 부분은 Controller에서 로직을 처리하는 과정에서 필요한 데이터를 처리하는 부분입니다.
데이터를 원하는 형태로 가공한다던지.. 실제로 Repository단까지 내려가지 않고 데이터를 거르는 부분입니다.

그리고 Exception 처리는 Controller에서 되도록이면 하지 않는 편이라는 말은.. 
오류를 처리하는 logic을 따로 작성하여 사용한다는 말입니다.

Controller에서 처리해도 상관없지만 개인적으로는 너무 복잡하거나 지저분한 코드를 싫어해서^^;;

오류가 발생하면 오류를 따로 처리하고 처리할 수 없는 경우는 View단으로 정보를 던저거나 합니다.
(처리한다는건 해당 오류를 페이지에 보이지 않고 정상적으로 처리하는 과정을 말합니다)

저처럼 하는 방법도있다는 걸 참고하시라고 말씀드리는겁니다.^^;;

2012년 3월 19일 오후 1:11, Sungchul Park <gyu...@gmail.com>님의 말:

이성현(kemuel)

unread,
Mar 19, 2012, 2:39:15 AM3/19/12
to Korea Spring User Group
> 되도록이면 Controller에서는 Exception 처리를 하지 않는 편이 좋다고 봅니다.
저는 Controller에서 try catch 문이 들어 가서 좋지 않다기 보다는
Business Logic이 들어 가는게 아닌가해서 그렇게 생각 했습니다.
Business Logic이 변경되면 Controller가 변경되는거 아닌가 해서요.
Controller는 DispatcherServlet가 Service에 맵핑하는 중간 역할이라 생각 하고 있습니다.
2번째 방법은 true/false 합니다.
만약 Business 로직이 Member 중복을 허용으로 바뀌면 Controller는 변경이 없을수 있겠죠(?)
Exceptoin 처리 전략을 프레임 워크로 해결 할 수 있다면 if else 또한 사라질 수 있지 않을 까 했습니다.
이 방법은 저의 개인적인 생각입니다. 저두 지금 현재 구글링 하며 자료들을 찾고 있는 중이에요..

Jihwan Kim

unread,
Mar 19, 2012, 7:09:09 PM3/19/12
to ks...@googlegroups.com
저같은 경우는 익셉션 처리를 SimpleMappingExceptionResolver 를 상속받아
CustomSimpleMappingExceptionResolver 를 구현하여 예외처리를 한곳으로 모았습니다.

예외는 예외상황별로 runtime 익셉션을 상속받아 정의하였구요.

헤더타입(Accept)에 따라 결과(예외코드 및 오류 메시지)처리를 하였습니다. (html, json, xml)

간혹 라이브러리에서 checked exception을 발생하는 메소드 같은 경우는 wrapping하여 runtime익셉션을
발생하도록 하였습니다.

위구조에서는 Controller에서는 try{} catch{} 문이 필요없구요. 서비스나 다오 레이어에서는 상황에 따라 있을
수 있지만 public method에서는 가독성을 위해서 지양하고 있습니다.

물론 예외가 아닌 정상플로우로 진행시켜야 하는 경우에는 컨트롤러에서 try catch가 필요할수도있겠으나 아직까지 그런 경우는
없었습니다.(이 경우는 controller 보다는 서비스 레이어에서 처리하는게 낫지 않을까 생각합니다.


2012년 3월 19일 오후 3:39, 이성현(kemuel) <kemu...@gmail.com>님의 말:

> --
> Google 그룹스 'Korea Spring User Group' 그룹에 가입했으므로 본 메일이 전송되었습니다.
> 이 그룹에 게시하려면 ks...@googlegroups.com(으)로 이메일을 보내세요.

> 그룹에서 탈퇴하려면 ksug+uns...@googlegroups.com로 이메일을 보내주세요.

Jihwan Kim

unread,
Mar 19, 2012, 7:13:08 PM3/19/12
to ks...@googlegroups.com
아 덧붙이면 이성현님 말씀대로 예외 발생은 모두 서비스단에서 하고 있습니다.(비즈니스 예외)

저도 비즈니스 로직을 어디에 두어야 하나, (컨트롤러?, 서비스?, 컨트롤러 + 서비스?) 고민이 많았습니다.

현재는 사이트 성격에 따라 좀 달라질수 있다고 생각하여 그부분에서는 자유도를 두고 있습니다.

하지만 동일한 사이트에서는 일관성있어야 겠죠.


2012년 3월 20일 오전 8:09, Jihwan Kim <jhki...@gmail.com>님의 말:

이재일

unread,
Mar 20, 2012, 4:27:25 AM3/20/12
to ks...@googlegroups.com
대충 비슷한 의미이긴 한데요.

요세 리팩토링에 심취해 있다보니. 살짝 코드가 변하는게 눈에 보이네요. 

저라도 처음에는 Controller단에서 try{} catch(){}로 자연스럽게 DuplicateMemberException로 처리를 했을거 같습니다. 하지만, 다른 컨트롤러에서도 같은 방식으로 try {}catch(){}로 로 감싸줬다면. 중복이 발생하니 MemberService 에 메소드는 throws로 DuplicateMemberException선언이 되어 자연스럽게 흘러가지 않았을까 하네요.

딱히 이건 이렇게 하라는것보다는 자연스럽게 어디다 놓으면 좋을지 정해지는면 되는게 아닌가 싶습니다. 

2012년 3월 19일 오전 11:30, Sungchul Park <gyu...@gmail.com>님의 말:
--
Google 그룹스 'Korea Spring User Group' 그룹에 가입했으므로 본 메일이 전송되었습니다.
이 그룹에 게시하려면 ks...@googlegroups.com(으)로 이메일을 보내세요.
그룹에서 탈퇴하려면 ksug+unsubscribe@googlegroups.com로 이메일을 보내주세요.

고종봉

unread,
Mar 20, 2012, 10:55:35 AM3/20/12
to ks...@googlegroups.com
원론적인,, 그리고 실제로도 좋은 설계가 될 수 있을 (것으로 생각하는) 개념을 말씀 드리면,

DAO, Service, Controller 모두 각 단에서 적잘한 예외 처리를 해야 한다고 생각합니다.

DAO에서는 DB Access가 주요 관심사항이니까,

duplicated key가 발생하면,, (이건 DB 제약에 따라 발생하겠죠?) DuplicatedKeyException을 던지는게 적절하다 보구요.

Service 에서는, DAO의 DuplicatedKeyException을 받아서 DuplicatedMemberException을 던져야 되구요. 
(!! DB, DAO 관점에서는 key가 중복된거지,, 걔가 멤버인지,, 머시기인지는 모르는게 맞다고 봅니다. 걔가 멤버고,, 멤버는 중복되면 안된다는 스펙은 서비스 로직에 해당된다고 봅니다.)

그리고, Controller에서는,, 서비스 로직 처리 중에 예외가 발생했으니,, 컨트롤러 단의 예외 처리를 
해야 한다고 봅니다.. 기본적으로 예외가 발생하지 않는게 정상 플로우고,, 예외가 발생하면 발생
예외별로 메시지를 표시한다고 하면,

==========  MemberController   =============================
@RequestMapping("/member/registmember.do")
public String registMember(Member member) {
try {
  memberService.registMember(member);
} catch(DuplicatedMemberException dme) {
  alertAndReturn("이미 등록된 회원입니다."); // 화면 alert 처리 
} catch(Exception e) {
  alertAndReturn("처리 중에 오류가 발생했습니다."); // 화면 alert 처리
}
 return "success"; 
}

2012년 3월 20일 오후 5:27, 이재일 <son...@gmail.com>님의 말:
그룹에서 탈퇴하려면 ksug+uns...@googlegroups.com로 이메일을 보내주세요.

이재일

unread,
Mar 25, 2012, 10:05:03 PM3/25/12
to ks...@googlegroups.com
spring mvc는 ExceptionResolver가 있잖아요(다른mvc모델도 비슷한게 있던데...). Controller에서는 의미있는 Exception을 던지면 그만이죠. 그래야 내 Exception답지!!

2012년 3월 20일 오후 11:55, 고종봉 <mercu...@gmail.com>님의 말:

Sanghyuk Jung

unread,
May 12, 2012, 11:05:00 AM5/12/12
to ks...@googlegroups.com
  안녕하세요, 답글을 달고 싶었던 글이였는데, 한동안 정신없이 살다가 이제 정신을 차릴만해서 뒤늦게 다시봤습니다. 이미 다른 분들이 많은 의견을 내었지만, '내가 진행하는 프로젝트에서라면 어떻게 할 것인가.'는 관점에서 정리해보았습니다.

1. Spring을 쓴다면 DAO에서는 아무런 예외처리를 안 할 것 같습니다.
 DAO는 DB호출만을 담당하고 파라미터에 따라서 쿼리가 달라지는 경우가 아니라면 조건문이나 예외처리를 안 두는 것을 선호합니다. 그리고 DuplicateMemberException은 Business Exception이라고 생각해서  Service layer부터 나타나도록 할 것 같습니다.

2. Service layer에서 DuplicateKeyException 을 잡아서 DuplicateMemberException으로 변환하겠습니다. 저는 여기서는 true, false로 값을 넘기지 않고, Exception을 Controller로 넘기는 선택을 하겠습니다.
 회원가입이 실패하는 다른 이유의 Exception(옛날에 불량회원이였다던지, 같은 주민등록번호로 다른 아이디가 있다던지)도 생길만하다면 'alternative return value'로서의 의미도 있지않나하는 생각도 듭니다. 여기서는 return type이 void라서 return type으로 Enum을 넘기는 선택도 가능하겠지만, 회원가입이 실패하는 상황이 처음에 다 정의되지 않았다면 우선은 Exception으로 넘기겠습니다.

만약 DuplicateMemberException일 때 또 다른 DAO나 Service를 호출하는 등의 비지니스 로직이 있다면 그것도 Service의 catch 절에서 처리합니다. 그런것들은 비니지스 로직으로 봐야겠죠.

3. Controller에서는 되도록 ExceptionResolve같은 것이나 servlet의 에러페이지로 넘기는 것으로 처리하겠습니다. 에러 페이지를 보여주는 것만으로 충분하지 않고 별도로 java코드에서 UI 때문에 할 일이 있다면 catch절이 부득이하게 나타날지도 모르겠습니다. 저도 controller단에 조건문이나 try catch가 나오는 상황을 좋아하지는 않습니다. Controller코드에 분기가 많다면 로드존슨 아저씨가 강조한 'clean and thin web tier' 스럽지 않다고 느껴지니까요. 그런데 그런 처리가 페이지 네비게이션을 위한 것이라면 충분히 controller의 역할로 맡겨도 된다고 생각합니다.


tier간의 역할분담 측면에서도 고민이 되지만,  Exception이나 return type이냐의 문제는 언제나 어렵게 느껴닙니다. 예를 들면 queryForObject 같이 1건을 조회하는 목적으로  DB를 select했을 때 아무것도 조회되지 않았을 때 iBatis에서는 null을 던지지만 Spring에서는 EmptyResultDataAccessException을 던집니다. JPA의 EntityNotFoundException도 유사한 Exception이구요. 잘 모르는 분야이지만, MSDN에 나와있는 Win32 API는 대부분 return type으로 성공실패 여부를 포함한 상태 객체를 던지고 다른 데이터들은 메소드 파리미터에 담아서 넘기게 되어 있다고 들은적도 있습니다.

암튼 늘 고민되는 문제지만, 어떤 방식이든 같은 프로젝트 안에서만이라도 일관성 있는 규칙을 정하는 것이 중요한듯합니다.

2012년 3월 26일 오전 11:05, 이재일 <son...@gmail.com>님의 말:
Reply all
Reply to author
Forward
0 new messages