Upload
eb-styles
View
2.045
Download
9
Embed Size (px)
Citation preview
보다 나은웹 애플리케이션 설계
@EBvi
이 문서는 spring과 myBatis를 처음 도입하는 팀을 위해 작성
되었습니다
spring을 선택해야 하는 이유
왜 spring을 써야 합니까?
결코 쉽지 않은 질문입니다만
차근차근 이유를 알아가보도록 합시다
우선 별로 spring과 관계없는 것부터 시작해보죠
우리가 그동안 작성해왔던소스코드
입력값 검증
서버단에서 사용자에게에러를 보여줘야할 의무가
있습니까?
보여줄 필요가 있는 것만 구현
그렇다면 반드시 필요한파라메터는 어떻게 검증?
@RequestParam
spring에서는 이렇게 간단하게 지정
파라메터가 들어오지 않으면아예 접근되지 않음
그래도 사용자에게 알려줘야 하지 않을까요?
jQuery로 간단하게 해결
alt 속성을 가진input 태그만을 검증
필드가 삭제되거나 늘어나도 검증부를 수정할 필요가전혀 없습니다
기존소스는 필드가 변경되면javascript 오류가날 수 있었죠
위험요소를 원천적으로 차단하는 것이 좋은 설계입니다
이 원칙은 대단히 중요합니다
Expression Language
왜 EL 태그를 써야 할까요?
웹 티어에 비지니스 로직이절대로 존재해서는 안된다
사실 이정도는비지니스 로직이 포함되었다고
보기 어렵습니다만
모 회사 모 프로젝트의 소스
중간중간 로직은 물론 데이터베이스까지 접근하고 있는 악질코드입니다
이런 소스코드의 디자인을수정하라고 하면
그냥 새로 만드는 게 더 쉽습니다
로직 없이EL만 들어간 깔끔한 코드
로직이 비지니스단에만존재하게 되면 프리젠테이션의 변경이 매우 쉬워집니다
어쩔 수 없는 특별한 상황을제외하고 비지니스 로직은비지니스단에만 구현합시다
Duplicate code
중복된 코드는 쓸데없는 데다가 코드를 확장, 유지하기 어렵게 만드는 주범입니다
등록폼과 수정폼을 나눠서 만들었었죠이 소스간의 차이는 그리 크지 않습니다
그렇다면 하나로 합치면 안될까요?
jsp에서는 attribute에 담겼는지 아닌지만 알면 됩니다
담겨있지 않으면 등록담겨있으면 수정
해당 객체가 null일 경우 그냥 출력되지 않습니다
action의 주소도 조건에 따라 가리키게 만듭시다
단 EL에서는 IF ELSE가 없기 때문에 choose를 사용할 수 밖에 없습니다
사실 FN을 정의해서 만들면 되지만 이건 나중에
폼의 submit을 제어할 땐 이렇게 구현하세요
아참! 이거 중요합니다
그동안은 이미지가 클릭될 때 submit되는 javascript 함수를 구현했었죠
이전 소스
이렇게 만들면 다양한 문제가 발생합니다
그중에서도 특히 사용성, 접근성이 크게 떨어지게 됩니다
input[type=image]로 구현
서버에서 검증할 거니까 잘못 등록될 수 없습니다
javascript를 오용하고 있는대표적인 사례입니다
Dispatcher Servlet
spring은 기본적으로모든 서블릿을 가로채 가도록
설정합니다
이렇게 하면 나머지 동작은spring이 모두 보증합니다
보증한다는 의미는 더이상 여러분이 신경쓰지 않아도 된다
는 의미입니다
UTF-8에서 한글처리는filter를 사용
이렇게 설정해도 get으로 보낼 때 한글이 깨지는 경우가 생길
수 있습니다
그 경우는 server.xml의 charset을 utf-8로 변경하면 됩
니다
Application Context
다양한 설정을 하는xml 형태의 파일입니다만
여기에서 bean을 선언하면서블릿을 가로챌 때 spring이
bean을 주입시킵니다
이제 매 서비스마다 new를쓸 필요가 없습니다
객체를 생성하고 주입하는 걸spring이 모두 보증합니다
왜 new를 선언하지 않는설계가 좋은 설계일까요?
왜냐하면
new를 선언하는 순간
인스턴스를 생성하게 되고
해당 클래스는
반드시 인스턴스에 해당하는 클래스를 알아야 하게 됩니다
즉, 의존성이 생겨나게 됩니다
의존성이 생겨나는 것은왜 문제가 될까요?
그냥 new하면 안되나요?
== 예제 코드 시현 ==
물론 interface도 해당 interface에 대한 의존성이 생
기는 것입니다
그러나 직접 구현한 클래스를 아는 것과 구상을 가리키는 것은 큰 차이가 있습니다
@Controller
먼저 spring에게 컨트롤러위치를 알려줍시다
spring은 web.xml에 지정한{name}-servlet.xml을
참조합니다
springapp으로 지정했으니까springapp-servlet.xml을 찾습니다
{name}-servlet.xml
이렇게 알려주면 spring이여기서 컨트롤러를찾게 됩니다
@Controller
@Controller로 선언된 클래스의 메서드는 각각의 action으로 정의할 수 있습니다
이전의 방식
각각의 ACTION을 관리하고IF ELSE(또는 SWITCH)로찾느라 복잡했었죠
이제 action resolver를구현할 필요가 없습니다
이렇게 구현하면 여러분은url만 보고 바로 검색해서액션을 찾을 수 있게 됩니다
View Resolver
view resolver를 사용하면 컨트롤러에서 jsp 파일을 리턴할 때 좀 더 편리하게 쓸 수 있습니다
이런식으로 뷰를 알려주죠
우선 물리적인 파일을 사용자에게 노출시키지 않기 위해
모든 jsp 파일을 /WEB-INF/jsp로 이동시킵시다
WEB-INF에 들어있는 파일은 외부에서 절대 접근할 수 없기
때문에
이렇게 만드는 것이 훨씬 안전합니다
외부에 노출되어서는 안되는 관리자 모드의 jsp 파일을 보여
줘선 안되겠죠?
그런데 이렇게 만들고 나니
매번 /WEB-INF/jsp/...와 같은 경로를 써줘야 하는 번거로움
이 생깁니다
{name}-servlet.xml
이렇게 뷰 리졸버를 정의해서 prefix와 postfix를 알려줄 수 있습니다
이렇게 하면 경로를 다시 쓸 필요없이 /WEB-INF/jsp/의 경로
를 가리키게 됩니다
RESTful
REST하지 않은 자원들
이러한 주소는 자원으로써의미가 전혀 없습니다
개발자 입장에서도 통일성이없기 때문에 중구난방으로생성되고 관리되게 됩니다
좋다고 해도 REST한 주소를만들기란 꽤 어려웠습니다
바보라서 안만든게 아니라 만드느라 복잡성이 더 증가했기 때문이죠
하지만
@RequestMapping을 사용해서정말 손쉽게 만들어낼 수 있습니다
직관적이고 통일성있는URL은 모두에게 좋습니다
Value Object Pattern
VO가 DTO와 어떤 차이가 있는 지에 대해선 조금 논의가 분분합니다. 이 문서엔 Core j2ee patterns를 따라 VO를 DTO와 동일한
패턴으로 정의합니다
이전 방식
HashMap에 일일히 담고검증하고 초기화했었습니다
만약 parameter가 변경된다면컨트롤러를 따라가며전부 수정해야 할 겁니다
추가-삭제가 빈번하게 일어난다면 HashMap에 담긴 자원이 유효하다는 걸 어떻게 보증하실 건가요?
HashMap은 편리하지만보증할 수가 없습니다
위험한 일은 아예 일어나지 않도록 원천적으로 차단합시다
private로 된 필드를 만듭시다
자동으로 get-set 메소드를만들어 줍시다
이러면 get-set 메서드가 자동으로 생성됩니다
이렇게 만들면spring이 매핑할 때
get-set 메서드를 지나갑니다
get-set 메소드를 만드는 것은 필수적입니다
만들지 않으면 주입할 수 있는 방법이 없습니다
만약 좀 더 제약을 걸고 싶으면get-set 메서드에서
해당 사항을 구현하면 됩니다
이러면 절대 null이 리턴되지 않고 xss에도 안전해지겠죠?
지금까지 오면서여러분이 느끼셨는지모르겠습니다만
제약을 건다는 건좀 더 단순해진다는 걸
의미합니다
반드시이렇게 만들어질 수 밖에 없다
반드시이것을 통과할 수 밖에 없다
반드시유일한 값이 들어가야만 한다
반드시로직이 존재해서는 안된다
반드시
...
이러한 제약은문제를 좀 더 단순하게 만드는좋은 설계의 원칙이 됩니다
예외가 생기면 문제가 복잡해집니다
우리는 그 예외까지도단순하게 설계해야 합니다
꼭 기억해주세요
아참, 이와 같은 객체를 POJO라고 부르고 이와 같은 패턴을
VO라고 부릅니다
POJO는 특정 기능, 프레임워크에 종속되지 않은 순수한 객
체를 말합니다
특정 구현을 상속 받거나 의존성이 있는 것도 아니고 그냥 아
무 것도 없죠
Data Access Object Pattern
마찬가지의 개념입니다
persistence단에서비지니스 로직은 절대 있어서
안됩니다
오직 데이터베이스와 관련된 행동만 보여주도록 만듭시다
이것을 객체지향 설계에서
단일책임 원칙(single responsibility principle)이라
고 합니다
객체가 단 한 개의 책임만을 가져야 한다는 설계 원칙입니다
이게 전부입니다
우리는 return type과 parameter type만 알면
나머지는 전혀 신경 쓸 필요가 없습니다
그렇기 때문에 구현된 클래스가 아닌 인터페이스만 봐도 뭘 어떻게 구현했는지 알 수 있습니다
한 6개월 정도 지나서 모든 기억이 가물거리기 시작하면
이것만 봐도 DAO가 어떻게 구현됐는지 쉽게 떠올릴 수 있죠
그러면 이제 직접 쿼리를 수정하러 가 봅시다
applicationContext.xml
myBatis와 관련된 설정은이 지점에서만 확인하면 OK
sqlmap-config.xml
여긴 각종 별칭(alias)을 정의해편리하게 갖다 쓸 수 있음
별칭을 정하지 않으면 매번 package를 다 써줘야 하니 정해서 편리하게 이용합시다
*.xml
각각 분리해서 관리하는 것이 편리합니다
하나의 xml에서 모두 관리하게 되면 코드가 길어지고 검색하
기도 불편하겠죠
다만 중요한 원칙은 물리적인 데이터베이스 모델링에 맞추는
것이 아니라
논리적인 데이터베이스 모델링에 맞춰 나누는 것입니다
이러한 점은 VO 설계에도 마찬가지입니다
VO의 필드를 물리적인 데이터베이스 필드와 동일하게 맞추
지 마세요
컴퓨터는 기계지만 여러분은 사람입니다
컴퓨터가 융통성이 없다면 여러분이 융통성 있게 코드를 작
성하면 됩니다
물론 반드시 다르게 만들라는 말은 아닙니다
어째튼 데이터베이스의 필드명과 VO의 필드명이 다르므로
alias를 사용해서 매퍼에게 알려줘야 합니다
AS로 각각의 필드에 별칭을 만들어주세요
alias로 만든 이름과 vo의 이름이 같으면 myBatis가 set 메서드로 넣어서 리턴해줍니다
또한myBatis는 강력한 기능을 많이
가지고 있는데
dynamic query를 작성하기에도 매우 편리합니다
사실 대부분의 검색 조건은 파라메터가 있냐없냐 이거죠
myBatis는 data type을 반드시 알려줘야 하기 때문에
만약 처음 도입하게 된다면
parameter type과 return type 때문에 고생할 수도 있습
니다
매퍼와 관련해서 오류가 나는 것은 data type이 달라서 그렇
습니다
정리
소스 코드를중복하여 작성하지 말라
위험요소는원천적으로 차단하라
로직은 반드시 존재해야 하는 곳에만 존재해야 한다
한 객체에 하나의 책임을 갖도록 설계하라
특정 구현이 아닌 인터페이스에 맞춰 프로그래밍하라
후라이드 반 양념 반
Reference from• One on one J2EE design and
development
• Kent’s beck implementation patterns
• The practice of programming
• The psychology of computer programming
• Professional software development
• Java Development with the Spring Framework
• Spring in action
• Core J2EE patterns
고맙습니다