REST(Representational State Transfer)는 월드 와이드 웹과 같은 분산 하이퍼미디어 시스템을 위한 소프트웨어 아키텍처의 한 형식이다. 이 용어는 로이 필딩(Roy Fielding)의 2000년 박사학위 논문에서 소개되었다. 그는 하이퍼텍스트 전송 프로토콜(HTTP)의 주요 저자들 가운데 한 사람이다. 그 뒤로 이 개념은 네트워킹 문화에 널리 퍼졌다.

엄격한 의미로 REST는 네트워크 아키텍처 원리의 모음이다. 여기서 네트워크 아키텍처 원리란 리소스를 정의하고 리소스에 대한 주소를 지정하는 방법에 대한 개괄을 말한다. 간단한 의미로는, 도메인 지향 데이터를 HTTP위에서 SOAP이나 쿠키를 통한 세션 트랙킹 같은 부가적인 전송 레이어 없이, 전송하기 위한 아주 간단한 인터페이스를 말한다. 이 두 가지의 의미는 당연히 겹치는 부분과 충돌되는 부분이 있다. 필딩의 REST 아키텍처 형식을 따르면 HTTP 프로토콜을 사용하지 않은 채로 또 월드 와이드 웹에서 전송하지 않고도 아주 커다란 소프트웨어 시스템을 설계하는것도 가능하다. 또한, 리모트 프로시저 콜을 이용하는 대신에 간단한 XML과 HTTP 인터페이스(REST 원리에 부합하지는 않지만)를 이용해 설계하는 것도 가능하다. 현실 세계에서의 REST 용어에 대한 이러한 두 가지 의미는 기술 토론에서 종종 혼란을 야기한다.

필딩의 REST 원리를 따르는 시스템은 종종 RESTful이란 용어로 지칭된다. 열정적인 REST 옹호자들은 스스로를 RESTafrians 이라고 부른다.

[편집]원리

REST의 지지자들은 웹이 몇 가지 핵심 설계 원칙의 직접적인 결과로서 확장성과 성장성을 갖게 되었다고 주장한다.

  • 응용 프로그램의 상태와 기능들은 리소스들로 나뉜다.
  • 모든 리소스는 하이퍼미디어 링크를 사용하는 공통 문법을 이용하여 유일한 방식으로 주소를 지정한다.
  • 모든 리소스들은 클라이언트와 리소스 사이의 상태 전송을 위한 유일한 인터페이스를 공유한다. 다음과 같이 이루어져 있다.
    • 잘 정의된(well-defined) 명령어들의 제약 집합.
    • 콘텐츠 종류와, 코드 온 디멘드를 부분적으로 지원하는 제약 집합
  • REST 아키텍처에 적용된 6가지 제한 조건 (개별 컴포넌트의 구현은 자유롭게 디자인할 수 있다)
    • 클라이언트/서버 구조 - 유니폼한 인터페이스로서 분리되어야 한다
    • 무상태(Stateless) - 각 요청 간 클라이언트의 컨텍스트가 서버에 저장되어서는 안된다
    • 캐시 처리 가능(Cacheable) - WWW에서와 같이 클라이언트는 응답을 캐싱할 수 있어야 한다.
      • 잘 관리되는 캐싱은 클라이언트-서버 간 interaction을 부분적으로 또는 완전하게 제거하여 scalability와 performance를 향상시킨다.
    • 계층화(Layered System) - 클라이언트는 보통 대상 서버에 직접 붙었는지, 또는 중간의 intermediary 서버와 붙었는지를 알 수 없다. Intermediary 서버는 로드 밸런싱을 가능하게 하거나 공유 캐시를 제공함으로써 시스템 scalability를 향상시키는 데 유용하다.
    • Code on demand (optional) - 자바 애플릿이나 자바스크립트의 제공을 통해 서버가 클라이언트가 실행시킬 수 있는 로직을 전송하여 기능을 확장시킬 수 있다.
    • Uniform Interface - 아키텍처를 단순화시키고 decouple시켜서 클라이언트, 서버 각 파트가 독립적으로 개선될 수 있도록 해준다.
  • REST 인터페이스의 원칙에 대한 가이드
    • 리소스의 구별
      • 개별적인 리소스는 요청에서 구별된다 - 웹 기반의 REST 시스템에서의 URI의 사용을 예로 들 수 있다. 리소스 그 자체는 클라이언트로 리턴되는 representation으로부터 개념적으로 분리되어 있다. 예를 들어, 서버는 그 데이터베이스를 전송하지 않는다, 단지 아마 어떤 데이터베이스 레코드를 나타내는 HTML, XML이나 JSON 등이 요청에서 구체적으로 요구되거나 서버의 구현에 따라서 예를 들어, 프랑스 어로, UTF-8로 인코딩되어 보내질 것이다.
    • 이들 representation을 통한 리소스의 조작
      • 클라이언트가 어떤 메타데이터가 첨부된 상태로 어떤 리소스의 representation을 가지고 있을 때, 만약 승인이 있다면, 서버에 있는 그 리소스를 변경 또는 삭제할 수 있는 충분한 정보를 가지고 있는 것이다.
    • 자기-서술적 메시지
      • 각 메시지는 자신을 어떻게 처리해야 하는지에 대한 충분한 정보를 포함해야 한다 - 예를 들어 어떤 파서를 불러야 하는지. 그 한 예는 MIME type과 같은 인터넷 미디어 타입의 사용을 들 수 있다. 미디어 타입만 가지고도, 클라이언트는 어떻게 그 내용을 처리해야할 지 알 수 있어야 한다. 메시지를 이해하기 위해 그 내용까지 살펴봐야 한다면, 그 메시지는 자기-서술적이 아니다. 예를 들어, 단순히 "application/xml"이라는 미디어 타입은, 코드 다운로드가 사용되지 않으면, 그 내용을 가지고 무엇을 해야할지에 대해 충분히 알려주지 못한다.
    • 애플리케이션의 상태에 대한 엔진으로서 하이퍼미디어
      • 만약에 클라이언트가 관련된 리소스에 접근하기를 원한다면, 리턴되는 representation에서 구별될 수 있어야 한다. 충분한 컨텍스트 속에서의 URI를 제공해주는 하이퍼텍스트 링크의 예를 들 수 있겠다.
  • REST 의 주요한 목표
    • 컴포넌트의 상호 연동 상의 확장성(scalability of component interactions)
    • 인터페이스의 범용성(Genrality of interfaces)
    • 컴포넌트의 독립적인 배포(Independent deployment of components)
    • 지연을 감소시키고, 보안을 강화하고, 레거시 시스템을 인캡슐레이션 시키는 중간 컴포넌트(Intermediary components to reduce latency, enforce security and encapsulate legacy systems)

[편집]같이 보기

[편집]바깥 고리






Representational state transfer (REST) or RESTful Web services are one way of providing interoperability between computer systems on the Internet. REST-compliant Web services allow requesting systems to access and manipulate textual representations of Web resources using a uniform and predefined set of stateless operations. Other forms of Web service exist, which expose their own arbitrary sets of operations such as WSDL and SOAP.[1] "Web resources" were first defined on the World Wide Web as documents or files identified by their URLs, but today they have a much more generic and abstract definition encompassing every thing or entity that can be identified, named, addressed or handled, in any way whatsoever, on the Web. In a RESTful Web service, requests made to a resource's URI will elicit a response that may be in XMLHTMLJSON or some other defined format. The response may confirm that some alteration has been made to the stored resource, and it may provide hypertext links to other related resources or collections of resources. Using HTTP, as is most common, the kind of operations available include those predefined by the HTTP verbs GET, POST, PUT, DELETE and so on. By making use of a stateless protocol and standard operations, REST systems aim for fast performance, reliability, and the ability to grow, by re-using components that can be managed and updated without affecting the system as a whole, even while it is running.

The term representational state transfer was introduced and defined in 2000 by Roy Fielding in his doctoral dissertation.[2][3] Fielding used REST to design HTTP 1.1 and Uniform Resource Identifiers (URI).[4][5][6] The term is intended to evoke an image of how a well-designed Web application behaves: it is a network of Web resources (a virtual state-machine) where the user progresses through the application by selecting links, such as /user/tom, and operations such as GET or DELETE (state transitions), resulting in the next resource (representing the next state of the application) being transferred to the user for their use.

History[edit source]

Roy Fielding speaking at OSCON 2008

REST was defined by Roy Fielding in his 2000 PhD dissertation "Architectural Styles and the Design of Network-based Software Architectures" at UC Irvine.[2] Fielding developed the REST architectural style in parallel with HTTP 1.1 of 1996–1999, based on the existing design of HTTP 1.0[7] of 1996.

In a retrospective look at the development of REST, Roy Fielding said:

Throughout the HTTP standardization process, I was called on to defend the design choices of the Web. That is an extremely difficult thing to do within a process that accepts proposals from anyone on a topic that was rapidly becoming the center of an entire industry. I had comments from well over 500 developers, many of whom were distinguished engineers with decades of experience, and I had to explain everything from the most abstract notions of Web interaction to the finest details of HTTP syntax. That process honed my model down to a core set of principles, properties, and constraints that are now called REST.[7]

Architectural properties[edit source]

The architectural properties affected by the constraints of the REST architectural style are:[2][8]

  • Performance - component interactions can be the dominant factor in user-perceived performance and network efficiency[9]
  • Scalability to support large numbers of components and interactions among components. Roy Fielding, one of the principal authors of the HTTP specification, describes REST's effect on scalability as follows:

REST's client–server separation of concerns simplifies component implementation, reduces the complexity of connector semantics, improves the effectiveness of performance tuning, and increases the scalability of pure server components. Layered system constraints allow intermediaries—proxiesgateways, and firewalls—to be introduced at various points in the communication without changing the interfaces between components, thus allowing them to assist in communication translation or improve performance via large-scale, shared caching. REST enables intermediate processing by constraining messages to be self-descriptive: interaction is stateless between requests, standard methods and media types are used to indicate semantics and exchange information, and responses explicitly indicate cacheability.[2]

  • Simplicity of a Uniform Interface
  • Modifiability of components to meet changing needs (even while the application is running)
  • Visibility of communication between components by service agents
  • Portability of components by moving program code with the data
  • Reliability is the resistance to failure at the system level in the presence of failures within components, connectors, or data[9]

Architectural constraints[edit source]

There are six guiding constraints that define a RESTful system.[8][10] These constraints restrict the ways that the server may process and respond to client requests so that, by operating within these constraints, the service gains desirable non-functional properties, such as performance, scalability, simplicity, modifiability, visibility, portability, and reliability.[2] If a service violates any of the required constraints, it cannot be considered RESTful.

The formal REST constraints are as follows:

Client-Server[edit source]

The first constraints added to our hybrid style are those of the client-server architectural style, described in Section 3.4.1. Separation of concerns is the principle behind the client-server constraints. By separating the user interface concerns from the data storage concerns, we improve the portability of the user interface across multiple platforms and improve scalability by simplifying the server components. Perhaps most significant to the Web, however, is that the separation allows the components to evolve independently, thus supporting the Internet-scale requirement of multiple organizational domains.[2]

Stateless[edit source]

The client–server communication is constrained by no client context being stored on the server between requests. Each request from any client contains all the information necessary to service the request, and session state is held in the client. The session state can be transferred by the server to another service such as a database to maintain a persistent state for a period and allow authentication. The client begins sending requests when it is ready to make the transition to a new state. While one or more requests are outstanding, the client is considered to be in transition. The representation of each application state contains links that may be used the next time the client chooses to initiate a new state-transition.[11]

Cacheable[edit source]

See also: Web cache

As on the World Wide Web, clients and intermediaries can cache responses. Responses must therefore, implicitly or explicitly, define themselves as cacheable, or not, to prevent clients from reusing stale or inappropriate data in response to further requests. Well-managed caching partially or completely eliminates some client–server interactions, further improving scalability and performance.

Layered system[edit source]

See also: Layered system

A client cannot ordinarily tell whether it is connected directly to the end server, or to an intermediary along the way. Intermediary servers may improve system scalability by enabling load balancing and by providing shared caches. They may also enforce security policies.

Code on demand (optional)[edit source]

Servers can temporarily extend or customize the functionality of a client by the transfer of executable code. Examples of this may include compiled components such as Java applets and client-side scripts such as JavaScript.

Uniform interface[edit source]

The uniform interface constraint is fundamental to the design of any REST service.[2] The uniform interface simplifies and decouples the architecture, which enables each part to evolve independently. The four constraints for this uniform interface are

Identification of resources
Individual resources are identified in requests, for example using URIs in Web-based REST systems. The resources themselves are conceptually separate from the representations that are returned to the client. For example, the server may send data from its database as HTMLXML or JSON, none of which are the server's internal representation.
Manipulation of resources through representations
When a client holds a representation of a resource, including any metadata attached, it has enough information to modify or delete the resource.
Self-descriptive messages
Each message includes enough information to describe how to process the message. For example, which parser to invoke may be specified by an Internet media type (previously known as a MIME type).[2]
Hypermedia as the engine of application state (HATEOAS)
Having accessed an initial URI for the REST application—analogous to a human Web user accessing the home page of a website—a REST client should then be able to use server-provided links dynamically to discover all the available actions and resources it needs. As access proceeds, the server responds with text that includes hyperlinks to other actions that are currently available. There is no need for the client to be hard-coded with information regarding the structure or dynamics of the REST service.[12]

Applied to Web services[edit source]

Web service APIs that adhere to the REST architectural constraints are called RESTful APIs.[13] HTTP-based RESTful APIs are defined with the following aspects:[14]

  • base URL, such as http://api.example.com/resources/
  • an internet media type that defines state transition data elements (e.g., Atom, microformats, application/vnd.collection+json,[14]:91–99 etc.) The current representation tells the client how to compose requests for transitions to all the next available application states. This could be as simple as a URL or as complex as a Java applet.[15]
  • standard HTTP methods (e.g., OPTIONS, GET, PUT, POST, and DELETE)[16]

Relationship between URL and HTTP methods[edit source]

The following table shows how HTTP methods are typically used in a RESTful API:

HTTP methods
Uniform Resource Locator (URL)GETPUTPOSTDELETE
Collection, such as http://api.example.com/resources/List the URIs and perhaps other details of the collection's members.Replace the entire collection with another collection.Create a new entry in the collection. The new entry's URI is assigned automatically and is usually returned by the operation.[17]Delete the entire collection.
Element, such as http://api.example.com/resources/item17Retrieve a representation of the addressed member of the collection, expressed in an appropriate Internet media type.Replace the addressed member of the collection, or if it does not exist, create it.Not generally used. Treat the addressed member as a collection in its own right and create a new entry within it.[17]Delete the addressed member of the collection.

The GET method is a safe method (or nullipotent), meaning that calling it produces no side-effects: retrieving or accessing a record does not change it. The PUT and DELETE methods are idempotent, meaning that the state of the system exposed by the API is unchanged no matter how many times the same request is repeated.

Unlike SOAP-based Web services, there is no "official" standard for RESTful Web APIs.[18] This is because REST is an architectural style, while SOAP is a protocol. REST is not a standard in itself, but RESTful implementations make use of standards, such as HTTPURIJSON, and XML.[18]

See also[edit source]



source - https://en.wikipedia.org/wiki/Representational_state_transfer








최근에 “Lorna Mitchell” 라는

“Five Clues That Your API isn’t RESTful (당신의 API가 RESTful 하지 않는 5가지 증거)”

의 내용을 기본으로 저의 견해를 덧붙여서 재구성 해봤습니다.


참고로 “Lorna Mitchell” 여성 PHP 개발자 이면서, 특히 API 디자인에 대해서

좋은 아이디어 와 글을 포스팅 하고 있습니다.

그녀의 블로그 :http://www.lornajane.net/


개인적으로 일을 하고 있는 도메인 특정상 많은 시스템  과 다양한 업체의

사람들 과 API 연동을 합니다.


“API 정의서“를 보면 서두에 “본 API 정의서는 RESTful 기반에…부랴부랴“로 시작을

합니다.

예전에는 “TCP/IP 기반 소켓 통신” 과 “RESTful“이 방식이 아닌

각자 회사 또는 팀에서 개별적으로 정의한 “HTTP/XML “를 사용을 했습니다.


하지만 최근에는 “RESTful” 아키텍쳐를 거의 대부분 사용을 하고 있습니다.

(물론 RESTful  방식이 기술이 아니고 API 디자인 아키텍쳐 입니다.  ^^)


하지만 막상 정의서의 연동 방식을 보면 “도대체 뭐가 RESTful”하게 설계를 했다는 거지?”

라는 의문점들이 생깁니다.


그래서 “100% RESTful 아키텍쳐링“을 다 지킬수는 없지만

최소한 아래에 “언급하는 5가지 디자인“에 대해서는 인식할 필요가 있습니다.


1. API의Endpoint가 오직 한개인가?

실제 경험한 사례로써 한번은 어떤 시스템의 연동 문서를 보니

“오직 한개의 URL로 fix” 되어 있었습니다.

예를 들어서 URL은 “http://example.com/rest” 이며, 모든 request는

해당 URL로 통합니다.


실제 자원에 대한 정보, 호출 서비스 정보, 파라미터 정보 등은 “XML 형태의 Body 데이터“로

전송 하도록 되어 있었습니다.


REST의 매우 중요한 구성 요소중 하나는 “Resource” 입니다.

“Resource”의 의미는 말 그대로 서비스를 제공 하는 시스템의 자원을 말합니다.

예를 들어서 “영화 예매 시스템“일 경우 “고객“, “예약번호“, “좌석번호“, “영화정보” 같은 것이며

대부분 명사(noun) 형태의 특성을 갖습니다.

또한 각 “Resource“는 “URL로 표시가 되며, 유일한 식별자“로써 표현 됩니다.


2. 모든 요청을 POST 방식으로만 요청 하는가?

“RESTful“에서 위에서 언급한 “Resource“를 핸드링하기 위해서 “HTTP의 전송 메서드”

를 사용합니다.

자원의 생성은 “POST“, 수정은 “PUT“, 조회는 “GET“, 삭제는 “DELETE” 메서드를 사용합니다.

예를 들면

예약 생성 : POST /reservation/2013012500001

예약 수정 : PUT /reservation/2013012500001

예약 조회 : GET /reservation/2013012500001

예약취소 : DELETE /reservation/2013012500001


하지만 실제 실무에서 이런 부분에 대해서 오해를 잘못해서

아래와 비슷한 방식으로 표현을 했었습니다.


예약 생성 : POST /reservation/2013012500001?cmd=insert

예약 수정 : POST /reservation/2013012500001?cmd=update

예약 조회 : POST /reservation/2013012500001?cmd=select

예약취소 : POST /reservation/2013012500001?cmd=delete


이와 같은 표현은 “RESTful“한 설계라고 할수 없습니다.


3. 응답에 대한 메타데이터를 Body에 포함 하는가?

“RESTful” 한 설계에서 “Request/Response“에 대한 메타 데이터는 최대한

HTTP 프로토콜의 방식을 준수 합니다.


클라이언트에서 요청 후 “처리 결과 값이 성공“일 경우 해당 시스템은

처리 결과를 “Body”에 포함하는 것이 아니고, HTTP Status의 값으로써

표현을 합니다.


저 같은 경우는 최대한 “Pure한 HTTP Status” 코드를 사용하고, 추가되는 부분은

별도로 “사용자 정의” 코드로 관리 합니다.


사용자 정보가 없을 경우는 : 404 (Not Found)

요청 정보가 정확하지 않을 경우 : 400 (Bad Request)

인증 실패 : 401 (UNAUTHORIZED)


즉, 이러한 전송에 대한 메타 데이터(결과 값, 세션 키)는 최대한 HTTP 헤더로

선언하고 실제 “Body 데이터“는 위에서 언급한 “Resource의 순수한 데이터”

만을 전송 해야 합니다.


4. URL에 동사(Verb)가 포함 되어 있는가?

위에서 언급 했듯이 “RESTful“한 API 설계에서 시스템에서 제공하는 것들은 “Resource“라고

말하며 “URL“로 표기 한다라고 말씀을 드렸습니다.

또한 이러한 “Resource“들은 “명사(noun)“적 특성을 같습니다.


만약 “URL“로 표기할때 동사(Verb)가 포함이 되면 혼돈이 올수 있습니다.

행위적 표현이기때문에 RPC(메서드)를 의미하는지 “Resource“를 하는지

구분이 모호해 집니다.


예를 들어서 예약 상태 정보를 조회를 하기 위해서 “/reservation/001/activate”

라는 표현 보다 “/reservation/001/status” 라고 표현을 해야 합니다.


5. URL에 RPC 호출 메서드 명이 있는가?

다양한 시스템 연동 스펙을 보면 아래 와 같이 실제 비지니스 컴포넌트

메서드를 URL에 포함되서 호출하는 경우가 있습니다.


?action=createReservation

?action=modifyReservation

?action=findReservation

?action=removeReservation


또한 URL에 노출되지는 않지만 “Body“안에 “XML” 형태로 선언 하는 경우도 있습니다.


하지만 “RESTful” 설계의 컨셉은 “RPC (Remote Procedure Call)” 목적이 아니고

시스템에서 제공하는 “Resource“를 “bucket“화 하는 것입니다.



My Thought

실제 실무에서 외부연동을 하면 “RESTful” 방식이라고 하지만 실제로는

위에서 언급한 5가지중 몇개를 지키지 않은 경우도 있고, 심지어 5개 모두

적용이 안되는 경우가 많습니다. 또한 HTTP/JSON/XML 통신만 하면

“RESTful” 하다고 생각들을 하는 것 같습니다.


개인적으로 아쉬운 부분은 약간의 시간을 내서 검토만 했었으면 어떨까 생각 듭니다.

그런데 필자도 말했지만 모든 연동 시스템이 반드시 “RESTful” 해야 하는 것인가?

에 대해서는 저는 약간 다른 생각을 합니다.


너무 유행이 되어 버려서 요새는 반드시 연동은 “RESTful“로 하려고 해서

오히려 시스템을 복잡하게 만들거나,  심지어 시스템 아키텍쳐가 흔들리는 경우도

있습니다.


2년전 제가 “일본 결제 시스템” 과 연동 한적이 있었습니다. 통신 방식은

“HTTP/XML” 이었습니다. 문서가 일본어로 되어 있어서 “번역기” 돌리고

추후에는 한글 번역본을 받아서 작업을 했는데..


연동 항목에 “Data Type, Length, Header 선언부, Null 유무” 등 깔끔하고

심플하면서, 쉽게 구성이 되었습니다.


구현은 어렵지 않았고 “결제 GW 테스트” 서버로 연동 테스트를 했는데

이해하고, 연동 테스트 하는데 3일정도 소요가 되었던것 같습니다.


반면 국내 다른 업체 연동을 할때 “RESTful” 방식으로 한다고 했는데

전혀 “RESTful” 하지도 않고 항목도 불명확해서 완전하게 테스트하는데

무려 한달이란 시간이 소요 되었고, 그 한달이란 기간이 정말 스트레스

엄청 받았었습니다.


또한 모든걸 “JSON“포맷으로 통신하는 것도 문제가 있습니다.

단말 <-> 서버는 “JSON” 포맷이 가볍고 좋을 수 있지만,

실제 “Server to Server” 연동은 가볍고, 빠른게 중요한게 아니라.

데이터의 정합성이 키포인트 입니다.


그럴 경우는 패캣이 다소 무거울수 있더라도 “XML” 포맷으로

해서 보내야 서로 비교 문석하기가 수월 합니다.


대량의 요청을 처리하는 서버는 하나의 바이트도 빼야 한다면

“A,B,C” 같은 토큰 방식으로 정의해서 보내야 하는 경우도 있습니다.


이렇듯 “연동 아키텍쳐링“을 할때


명분 없이 단지 “대세”라는 모호한 논리로 맞지도 않은 것을 도입하면

안된다고 생각 합니다.

저희는 개발자이고 아키텍트이기 때문에 “현 Usecase”에 맞는 것인지

냉정한 판단이 필요합니다.

“RESTful” 아키텍쳐는 “외부 OpenAPI“에 매우 적합한

연동 아키텍쳐인것 같습니다.

마지막으로 “차니 블로그“님께서 “API Strategy & Strata 2013 Conference” 참관기에서

했던 말을 남기며 본 글을 마무리 하겠습니다.


API 333 목표 : 3초만에 API를 이해하고, 30초만에 API 키를 발급 받아서, 

3분안에 첫번째 요청이 오도록 해라



source - https://beyondj2ee.wordpress.com/2013/03/21/%EB%8B%B9%EC%8B%A0%EC%9D%98-api%EA%B0%80-restful-%ED%95%98%EC%A7%80-%EC%95%8A%EC%9D%80-5%EA%B0%80%EC%A7%80-%EC%A6%9D%EA%B1%B0/






1. RPC에서 REST까지 간단한 개념소개 myrisinsun@gmail.com

2. •RPC •CORBA •RMI •SOAP •REST 이것들을 아주 아주 간단히 훑어볼거에요.

3. 시작하기전에.. Client-Server 모델 들어 보셨죠? 과거(?)에는
 Client가 Server에 있는 자료를
 요청하고, 받을때 주로 socket 프로그래밍을
 활용했습니다. CLIENT SERVER 주로.. LAN

4. socket 프로그래밍은..
 대부분의 프로그래밍 언어에서 
 API 형태로 제공을 합니다. 우리는 API Document의 사용법만 확인하고
 손쉽게 구현이 가능합니다. C socket.h JAVA java.net.Socket java.net.ServerSocket

5. server.java serverSocket = new ServerSocket(7000); socket = serverSocket.accept(); //TODO: socket으로부터 데이터를 받고, 보내고 socket.close(); serverSocket.close(); client.java socket = new Socket(’192.1.1.5’, 7000); //TODO: socket으로 데이터를 보내고, 받고 socket.close(); 위와같이 JAVA에서 제공하는 Class와 API를 이용해서
 간단한 코드만으로 작성이 가능합니다. 하지만..

6. 저런 코드가 정상 동작하려면..
 아래와 같은 상황이 보장되어야 합니다. • 네트워크는 언제나 빠르고 장애가 없음. • 서버는 클라이언트가 요청시 언제든 즉시 응답. • 클라이언트는 언제든 서버와 바로바로 연결 
 그러나 현실은..
 네트워크, 서버, 클라이언트 모두
 느려지고, 각종 문제가 발생할 수 있습니다.

7. 결국.. 발생 가능한 예외상황들을
 모두 예측을 하고
 대비(구현)해야만 합니다. 물론.. 그 모든것은 개발자의 몫!!

8. ServerClient 그래서(?) RPC가 등장합니다. RPC
 Remote Procedure Call 대략 이런 컨셉입니다. 프로그램A Client Stub
 
 프로그램B Server Stub (Skeleton) network 개발자가
 할일 RPC가 대신 해주는 일

9. 조금더 자세히 보자면.. Server에 있는
 프로그램 정보를 알고 있는 녀석(IDL)을
 활용해서 개발자는..
 network 통신과 관련된 작업은
 신경 쓰지 않아도 되고,
 원격지에 위치한 프로그램을
 로컬에 있는 프로그램처럼 사용 할 수 있습니다.
 
 
 IDL: Interface Definition Language

10. RPC에 대한 더 자세한 내용은
 아래 2개 링크를 추천. Introduction to Remote Procedure Call (RPC) - Abdelrahman Al-Ogail
 http://www.slideshare.net/ZiKaS/introduction-to-remote- procedure-call Remote Procedure Call - Roy Antony Arnold G
 https://middlewares.wordpress.com/2008/02/01/remote- procedure-call

11. 그리고 CORBA, RMI가 있습니다. CORBA: Common Object Request Broker Architecture
 로컬/원격을 포괄한 프로그램 객체 간의 메소드 호출 표준화에 목적으로 OMG에서 정의한 규격. 다양한 언어 지원. RMI: Remote Method Invocation
 서로 다른 JVM간의 메소드 호출을 지원. JAVA만 지원. 2가지 모두 RPC와 같이
 원격 프로그램을 로컬에 있는것처럼 사용가능 합니다.
 분산 환경에서의 remote call.

12. RMI, CORBA에 대한 설명은
 아래 링크 추천 RMI, CORBA and NetBeans - Sing Li
 http://www.developerfusion.com/article/84316/rmi- corba-and-netbeans Java RMI & CORBA. A comparison of two competing technologies
 http://www.javacoffeebreak.com/articles/rmi_corba 원격 객체 통신(RMI와 CORBA) - 윤경구
 http://www.javadom.com/tutorial/rmi-idl

13. 그런데
 RPC, RMI, CORBA를
 흔히? 볼수는 없습니다.
 (사실.. RPC와 그 개념은 여기저기서 사용 되고는 있습니다.) 왜일까?
 복잡성, 어려움, 보안, … 좀더 자세한 내용은 아래 링크를 추천 Why has CORBA lost popularity?
 http://stackoverflow.com/questions/3835785/why-has- corba-lost-popularity

14. 지금까지 본것처럼..
 socket을 활용해 통신을 하다가, 복잡한 네트워크와 하부영역에 대한
 캡슐화를 통해
 간단히(메소드, 펑션) 호출하려는 시도. 그리고,
 그다음은.. 우리에게 익숙한 
 web을 활용해보려는 시도로 이어집니다.

15. SOAP
 Simple Object Access Protocol 컨셉은 간단(?) 합니다. HTTP + XML 우리에게 익숙하고 각광받던 2가지 기술을 사용,
 간단히 ‘메세지’를 주고 받는것처럼 정보를 주고 받자. SOAP에서는 봉투(Envelope)라는 개념으로 부름. 간단한 그림으로 보면..

16. SOAP Simple Work Flow • Server는 서비스사용법(WSDL)을 작성해서 UDDI로 전송 • Client는 UDDI를 통해 서비스 목록을 확인(lookup)하고,
 사용법을 참고해서 메세지(SOAP형태)로 Server에게 요청. Client Server 서비스목록
 (UDDI) ……… ……lookup publish 요청 SOAP형식으로

17. 샘플 코드 보기 SOAP Message 예제
 http://ko.wikipedia.org/wiki/SOAP#SOAP_.EC.83.98.ED. 94.8C WSDL Document Example
 http://www.tutorialspoint.com/wsdl/wsdl_example.htm UDDI란?
 http://en.wikipedia.org/wiki/ Universal_Description_Discovery_and_Integration

18. 마지막으로 REST REST
 REpresentational State Transfer 그동안
 데이터의 요청/전달을 위해
 등장했던 기술들이 복잡하고 어려웠기에 우리에게 익숙한 web(HTTP)을
 있는 그대로 적용해보자는 시도.

19. REST를 간단히 정의해보자면 HTTP 1.1을 사용하며,
 URI를 자원(Resource)으로 표현하고
 처리결과를 Status Code로 사용하는 일종의 ‘스타일’을 뜻합니다. 그리고
 REST가 제안하는 스타일에 맞는다면
 RESTful하다 라고 말합니다.

20. HTTP 1.1 ? HTTP 1.0 에서는
 주로 get, post method를 사용했지만, HTTP 1.1 에서는 추가적으로
 put, delete method가 존재합니다. 그리고.. 다음과 같은 용도로 사용을 하게 됩니다. HTTP method CRUD (SQL) GET 조회 SELECT POST 등록 INSERT PUT 수정 UPDATE DELETE 삭제 DELETE

21. URI를 자원으로 표현? 쉽게 이야기하자면
 URI 에 동사없이
 명사(자원이름) 로만 구성을 해야합니다. 예를들면
 http://www.o.com/user/sunny/profile/address 참고로.. 위의 주소를
 get 방식으로 호출하면: 사용자 주소를 조회
 put 방식으로 호출하면: 사용자 주소를 수정
 delete 방식으로 호출하면: 사용자 주소 정보를 삭제

22. 처리결과를 Status Code 로 사용? 기존에는..
 http의 처리결과를
 header의 Status Code는 200 으로,
 그리고 body에 처리 결과를 작성. 하지만 REST에서는
 처리결과를 header의 Status Code를 활용. http
 header??
 body??

23. http로 전송되는 메세지의 구조 (request/response) Message Start-Line Header Field 공백줄 Message Body GET /user/sunny HTTP/1.1 Host: www.o.com Content-Length: 9 region=ko HTTP 1.1 404 Not Found Content-Length: 53 Content-Type: application/json Date: Mon, 26 Jan 2015 09:01:05 GMT { “ErrorDetail” : “사용자가 존재하지 않습 니다.” }

24. http 메세지 예제 /user/sunny 를 호출하지만, 존재하지 않는 사용자인 경우 GET /user HTTP/1.0 Host: www.o.com Content-Length: 12 userId=sunny HTTP 1.0 200 OK Content-Length: 55 Content-Type: text/html Date: Mon, 26 Jan 2015 09:01:05 GMT <html><body> 사용자가 존재하지 않습니다. </body></html> GET /user/sunny HTTP/1.1 Host: www.o.com Content-Length: 000 HTTP 1.1 404 Not Found Content-Length: 53 Content-Type: application/json Date: Mon, 26 Jan 2015 09:01:05 GMT { “ErrorDetail” : “존재하지 않는 사용자 정 보 입니다.” }

25. 그리고
 REST 방식을 사용할때(==RESTful 하게 구현할때) REST+XML 도 RESTful 입니다!
 다만, JSon이 용량도 적고 유용하기 때문에.. 마지막으로..
 RESTful 관련 추천링크입니다. 당신의 API가 RESTful 하지 않은 5가지 증거
 https://beyondj2ee.wordpress.com/2013/03/21/당신의-api 가-restful-하지-않은-5가지-증거


source - https://www.slideshare.net/WonchangSong1/rpc-restsimpleintro






'Web > Common' 카테고리의 다른 글

Adobe Flash  (0) 2012.05.11
색상표  (0) 2012.05.11
웹 어플리케이션(Web application)  (0) 2012.05.07
/favicon.ico 에 대해  (0) 2012.04.19
삼성 모바일OS '타이젠'…구글-모질라 제쳐  (0) 2012.04.08
Posted by linuxism
,


@RequestMapping을 적용한 Controller의 메소드는 아래와 같은 메소드 파라미터와 리턴 타입을 사용할수 있다.
특정 클래스를 확장하거나 인터페이스를 구현해야 하는 제약이 없기 때문에 계층형 Controller 비해 유연한 메소드 시그니쳐를 갖는다.

@Controller의 메소드 파라미터

사용가능한 메소드 파라미터는 아래와 같다.

  • Servlet API - ServletRequest, HttpServletRequest, HttpServletResponse, HttpSession 같은 요청,응답,세션관련 Servlet API들.
  • WebRequest, NativeWebRequest - org.springframework.web.context.request.WebRequest, org.springframework.web.context.request.NativeWebRequest
  • java.util.Locale
  • java.io.InputStream / java.io.Reader
  • java.io.OutputStream / java.io.Writer
  • @RequestParam - HTTP Request의 파라미터와 메소드의 argument를 바인딩하기 위해 사용하는 어노테이션.
  • java.util.Map / org.springframework.ui.Model / org.springframework.ui.ModelMap - 뷰에 전달할 모델데이터들.
  • Command/form 객체 - HTTP Request로 전달된 parameter를 바인딩한 커맨드 객체, @ModelAttribute을 사용하면 alias를 줄수 있다.
  • Errors, BindingResult - org.springframework.validation.Errors / org.springframework.validation.BindingResult 유효성 검사후 결과 데이터를 저장한 객체.
  • SessionStatus - org.springframework.web.bind.support.SessionStatus 세션폼 처리시에 해당 세션을 제거하기 위해 사용된다.

메소드는 임의의 순서대로 파라미터를 사용할수 있다. 단, BindingResult가 메소드의 argument로 사용될 때는 바인딩 할 커맨드 객체가 바로 앞에 와야 한다.

public String updateEmployee(...,@ModelAttribute("employee") Employee employee,			
			BindingResult bindingResult,...) <!-- (O) -->
 
public String updateEmployee(...,BindingResult bindingResult,
                        @ModelAttribute("employee") Employee employee,...) <!-- (X) -->

이 외의 타입을 메소드 파라미터로 사용하려면?
스프링 프레임워크는 위에서 언급한 타입이 아닌 custom arguments도 메소드 파라미터로 사용할 수 있도록 org.springframework.web.bind.support.WebArgumentResolver라는 인터페이스를 제공한다.
WebArgumentResolver를 사용한 예제는 이곳을 참고하라.

@Controller의 메소드 리턴 타입

사용가능한 메소드 리턴 타입은 아래와 같다.

  • ModelAndView - 커맨드 객체와 @ModelAttribute이 적용된 메소드의 리턴 데이터가 담긴 Model 객체와 View 정보가 담겨 있다.
            @RequestMapping(value = "/updateDepartment.do", method = RequestMethod.GET)
    	public ModelAndView formBackingObject(@RequestParam("deptid") String deptid) {
    		Department department = departmentService.getDepartmentInfoById(deptid);
    		ModelAndView mav = new ModelAndView("modifydepartment");
    		mav.addObject("department", department);
    		return mav;
    	}
    또는
    	public ModelAndView formBackingObject(@RequestParam("deptid") String deptid, ModelMap model) {
    		Department department = departmentService.getDepartmentInfoById(deptid);
    		model.addAttribute("department", department);
    		ModelAndView mav = new ModelAndView("modifydepartment");
    		mav.addAllObjects(model);
    		return mav;
    	}
  • Model(또는 ModelMap) - 커맨드 객체와 @ModelAttribute이 적용된 메소드의 리턴 데이터가 Model 객체에 담겨 있다.
    View 이름은 RequestToViewNameTranslator가 URL을 이용하여 결정한다. 인터페이스 RequestToViewNameTranslator의 구현클래스인 DefaultRequestToViewNameTranslator가 View 이름을 결정하는 방식은 아래와 같다.
    http://localhost:8080/gamecast/display.html -> display
    http://localhost:8080/gamecast/displayShoppingCart.html -> displayShoppingCart
    http://localhost:8080/gamecast/admin/index.html -> admin/index
    	@RequestMapping(value = "/updateDepartment.do", method = RequestMethod.GET)
    	public Model formBackingObject(@RequestParam("deptid") String deptid, Model model) {
    		Department department = departmentService.getDepartmentInfoById(deptid);
    		model.addAttribute("department", department);
    		return model;
    	}
    또는
    	@RequestMapping(value = "/updateDepartment.do", method = RequestMethod.GET)
    	public Model formBackingObject(@RequestParam("deptid") String deptid) {
    		Department department = departmentService.getDepartmentInfoById(deptid);
    		Model model = new ExtendedModelMap();
    		model.addAttribute("department", department);
    		return model;
    	}
  • Map - 커맨드 객체와 @ModelAttribute이 적용된 메소드의 리턴 데이터가 Map 객체에 담겨 있으며, View 이름은 역시 RequestToViewNameTranslator가 결정한다.
    	@RequestMapping(value = "/updateDepartment.do", method = RequestMethod.GET)
    	public Map formBackingObject(@RequestParam("deptid") String deptid) {
    		Department department = departmentService.getDepartmentInfoById(deptid);
    		Map model = new HashMap();
    		model.put("department", department);
    		return model;
    	}
    또는 
    	@RequestMapping(value = "/updateDepartment.do", method = RequestMethod.GET)
    	public Map formBackingObject(@RequestParam("deptid") String deptid, Map model) {
    		Department department = departmentService.getDepartmentInfoById(deptid);
    		model.put("department", department);
    		return model;
    	}
  • String - 리턴하는 String 값이 곧 View 이름이 된다. 커맨드 객체와 @ModelAttribute이 적용된 메소드의 리턴 데이터가 Model(또는 ModelMap)에 담겨 있다. 리턴할 Model(또는 ModelMap)객체가 해당 메소드의 argument에 선언되어 있어야 한다.
            <!--(O)-->
    	@RequestMapping(value = "/updateDepartment.do", method = RequestMethod.GET)
    	public String formBackingObject(@RequestParam("deptid") String deptid, ModelMap model) {
    		Department department = departmentService.getDepartmentInfoById(deptid);
    		model.addAttribute("department", department);
    		return "modifydepartment";
    	}
     
            <!--(X)-->
    	@RequestMapping(value = "/updateDepartment.do", method = RequestMethod.GET)
    	public String formBackingObject(@RequestParam("deptid") String deptid) {
    		Department department = departmentService.getDepartmentInfoById(deptid);
    		ModelMap model = new ModelMap();
    		model.addAttribute("department", department);
    		return "modifydepartment";
    	}
  • View - View를 리턴한다. 커맨드 객체와 @ModelAttribute이 적용된 메소드의 리턴 데이터가 Model(또는 ModelMap)에 담겨 있다.
  • void - 메소드가 ServletResponse / HttpServletResponse등을 사용해서 직접 응답을 처리하는 경우. View 이름은 RequestToViewNameTranslator가 결정한다.


출처 - http://www.egovframe.org/wiki/doku.php?id=egovframework:rte:ptl:annotation-based_controller&s[]=requestmapping


===================================================================================


스프링 2.x 에서는 AbstractController 를 상속받아 Controller를 구현하였으나 스프링 3.0부터는 Annotation(어노테이션)

이용하여 간단하게 Controller를 구현할 수 있다.

 

1. 기본 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Controller
public class HomeController {
    // "/home.do" 경로를 입력하면 이 메소드로 진입한다.
    @RequestMapping("/home.do")
    public ModelAndView home() {
  
        // 클라이언트에게 보여줄 jsp 페이지를 설정한다.
        ModelAndView mav = new ModelAndView("home");        // home.jsp
        mav.addObject("hello", "hello Spring!");            // key & value
         
        return mav;
    }
 
    @RequestMapping("/room.do")
    public String room() {
        return "room";          // room.jsp
    }
}


@RequestMapping 어노테이션의 값에는 해당 메소드가 처리할 URL을 정의한다.

두번째 메소드의 리턴 타입은 String인데 이것은 viewName을 의미한다.

* 리턴 타입의 종류 

 

2. GET/POST 에 따른 처리 방법

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Controller
public class NewArticleController {
 
    @RequestMapping(value = "/article/newArticle.do", method = RequestMethod.GET)
    public String form() {
        return "article/newArticleForm";
    }
 
    @RequestMapping(value = "/article/newArticle.do", method = RequestMethod.POST)
    public String submit(@ModelAttribute("command") NewArticleCommand command) {
        return "article/newArticleSubmitted";
    }
 
    public void setArticleService(ArticleService articleService) {
        this.articleService = articleService;
    }
}

만약 위처럼 동일한 URL에 대해서 처리한다고 하면 아래처럼 할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Controller
@RequestMapping("/article/newArticle.do")
public class NewArticleController {
      
    @RequestMapping(method = RequestMethod.GET)
    public String form() {
        ...
    }
 
    @RequestMapping(method = RequestMethod.POST)
    public String submit(@ModelAttribute("command") NewArticleCommand command) {
        ...
    }
    ...
}

만약 @RequestMapping 에 method 설정을 하지 않으면 GET/POST 등 모든 HTTP 전송방식을 처리한다.



3. Controller 자동스캔
@Controller 은 @Component 와 마찬가지로 컴포넌트 스캔 대상이다.
<context:component-scan> 태그를 이용해서 아래처럼 선언해서 어노테이션이 적용된 컨트롤러를 자동으로 로딩할 수 있다.

1
<context:component-scan base-package="패키지네임" />


출처 - http://warmz.tistory.com/697


===================================================================================


3.0 에서는 Controller 어노테이션을 이용해서 컨트롤러 클래스를 구현한단다. (과거에는 AbstractController 등을 썼음)

1. 기본구현
@Controller , @RequestMapping 어노테이션을 이용하면된다.

@Controller
public class HelloController {
@RequestMapping("/hello.do")
public String hello() {
            return "hello";
        }


@RequestMapping 어노테이션의 값에는 해당 메소드에서 처리할 URI가 무엇인지를 정의한다.
위의 경우 리턴값이 ModelAndView 가 아니라 String인데 이는 바로 이값이 ViewName이 된다.
이 뷰이름으로 ViewResolver 를 통해서 실제 view 파일을 가져와서 보여주겠지..


2. 전송방식... GET/POST
어차피... 이건 웹 어플리케이션이다. 전송하는 방식은 GET/POST 겠지.
같은 URL 이고 get/ post 의 다른 방식의 전송을 할때 다르게 처리가능하다.

@Controller
public class NewArticleController {
 
@RequestMapping(value="/article/newArticle.do", method = RequestMethod.GET)
public String form() {
return "article/newArticleForm";
}

@RequestMapping(value="/article/newArticle.do", method = RequestMethod.POST)
public String submit(@ModelAttribute("command") NewArticleCommand command) {
return "article/newArticleSubmitted";
}

public void setArticleService(ArticleService articleService) {
this.articleService = articleService;
}

}


만약 위처럼 처리하는 메소드가 동일한 URI에 대해서 처리하는것이라면 기본 URI를 지정할 수 도 있다.


@Controller
@RequestMapping("/article/newArticle.do")
public class NewArticleController {
 
@RequestMapping(method = RequestMethod.GET)
public String form() {
...
}

@RequestMapping(method = RequestMethod.POST)
public String submit(@ModelAttribute("command") NewArticleCommand command) {
...
}
...
}


만약 RequestMapping 어노테이션에 method 설정은 안하면 ,GET/POST 등 모든 HTTP 전송방식을 처리한다.



RequestMapping 에 대한 어노테이션 기능은 별도의 문서에 정리했다. 참고바란다.

3. 컨트롤러 메소드의 리턴타입?

매핑된 컨트롤러의 메소드가 해야할 일을 처리한다음... 뭔가를 리턴할것이다.
그 타입에 대한 설명이다.
@RequestMapping 메소드의 리턴타입

리턴타입 설명
ModelAndView 뷰 정보 및 모델정보를 담고 있는 ModelAndView 객체
Model 뷰에 전달할 객체 정보를 담고 있는 Model을 리턴한다. 이때 뷰 이름은 요청 URL로부터 결정된다.(RequestToViewNameTranslator 를 통해 뷰 결정)
 Map 뷰에 전달할 객체 정보를 담고 있는 Model을 리턴한다. 이때 뷰 이름은 요청 URL로부터 결정된다.(RequestToViewNameTranslator 를 통해 뷰 결정)
 String 뷰 이름을 리턴한다.
 View 객체View 객체를 직접 리턴, 해당 View 객체를 이용해서 뷰를 생성한다. 
 void메소드가 ServletResponse나, HttpServletResponse 타입의 파라미터를 갖는 경우 메소드가 직접 응답을 처리한다고 가정한다. 그렇지 않을 경우 요청 URL로 부터 결정된 뷰를 보여준다.(RequestToViewNameTranslator를 통해 뷰 결정) 
 @ResponseBody 어노테이션적용
메소드에서 @ResponseBody 어노테이션이 적용된 경우, 리턴객체를 HTTP응답으로 전송한다. HttpMessageConverter를 이용해서 객체를 HTTP 응답 스트림으로 변환한다. 


4. 컨트롤러 클래스 자동스캔
@Controller 어노테이션은 @Component 어노테이션과 마찬가지로 컴포넌트 스캔대상이다. 
<context:component-scan> 태그를 이용해서 아래처럼 선언해서 (xml) 어노테이션이 적용된 컨트롤러를 자동으로 로딩할 수 있다.

<context:component-scan base-package="madvirus.spring.chap06.controller" />


출처 - http://ezsnote.tistory.com/entry/Controller-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98

===================================================================================


@RequestMapping 어노테이션은 컨트롤러가 처리할 요청URL을 명시하는데 사용되며, 클래스나 메서드에 적용될 수 있다.


첫째로, @RequestMapping 어노테이션을 클래스에는 적용하지 않고 메서드에만 적용할 경우에는 


각각의 메서드가 처리할 요청 URL을 명시하게 된다.


import! org.springframework.stereotype.Controller;

import! org.springframework.ui.ModelMap;

import! org.springframework.web.bind.annotation.RequestMapping;

import! org.springframework.web.bind.annotation.ReqeustParam;


@Controller

pubilc class SearchAnnotController {

@RequestMapping("/annot/search/internal.do")

public String searchInternal(@RequestParam("query") String query, 

@RequestParam("pageNo") int pageNo, ModelMap modelMap) {

return search(internalSearchService, query, pageNo, modelMap);

}

@RequestMapping("/annot/search/external.do")

public String searchExternal(@RequestParam("query") String query, 

@RequestParam("pageNo") int pageNo, ModelMap modelMap) {

return search(externalSearchService, query, pageNo, modelMap);

}

}


다수의 메서드에 @RequestMapping 어노테이션을 적용하면, 

MultiActionController와 같이 한 개의 컨트롤러에서 다수의 요청을 처리할 수 있게 된다.


@RequestMapping 어노테이션의 method 엘리먼트를 이용하여 처리할 수 있는 HTTP METHOD 목록을 지정할 수도 있다.

@RequestMapping(value="/anno/search/external.do", method=RequestMethod.POST)

public String searchExternal(...) {

....

}

@RequestMapping 어노테이션을 클래스 타입에 적용하게 되면, 해당 컨트롤러 클래스는 지정한 URL만을 처리할 수 있게 된다.

이 경우 메서드에 적용되는 @RequestMapping 어노테이션은 더 이상 URL을 명시할 수 없으며, 

method엘리먼트와 params 엘리먼트만을 지정할 수 있게 된다.


예를 들어, SimpleFormController처럼 GET 요청이 들어오면 입력 폼을 출력하고 POST 요청이 들어오면 폼 전송을 처리하고 싶다면,

다음과 같이 클래스 타입에는 Mapping URL을 명시한 @RequestMapping 어노테이션을 적용하고, 각 메서드에서는 method

엘리먼트를 명시한 @RequestMapping 어노테이션을 적용하면 된다.

import! org.springframework.web.bind.annotation.RequestMapping;

import! org.springframework.web.bind.annotation.ReqeustMethod;


@Controller

@RequestMapping("/annot/login.do")

public class LoginAnnotController {
...

@RequestMapping(method = RequestMethod.GET)

public String setupForm(ModelMap map) {

...

}

@RequestMapping(method = RequestMethod.POST)

public String processSubmit(@ModelAttribute("login")

LoginCommand loginCommand, BindingResult erros, ModelMap model) {

...

}

}


출처 - http://blog.daum.net/_blog/BlogTypeView.do?blogid=0Ps1S&articleno=6&_bloghome_menu=recenttext#ajax_history_home


===================================================================================


@MVC 의 가장 큰 특징은 핸들러 매핑과 핸들러 어댑터의 대상이 오브젝트가 아니라 메소드라는 점이다. Controller 와 같이 하나의 메소드를 가진 인터페이스로 정의되는 컨트롤러는 특별한 방법을 사용하지 않는 한, URL 당 하나의 컨트롤러 오브젝트가 매핑되고 컨트롤러당 하나의 메소드만 DispatcherServlet 의 호출을 받을 수 있다. 반면에 @MVC 에서는 모든 것이 메소드 레벨로 세분화 됐다. 메소드 레벨에서 컨트롤러를 작성할 수 있게 된 기술적인 배경에는 애노테이션을 이용한 프로그래밍 모델이 있다. 애노테이션은 타입 레벨뿐 아니라 메소드 레벨에도 적용이 가능하기 때문이다.

@MVC 의 핸들러 매핑을 위해서는 DefaultAnnotationHandlerMapping 이 필요하다 DefaultAnnotationHandlerMapping 은 디폴트 핸들러 매핑전략이므로 다른 핸들러 매핑 빈을 명시적으로 등록하지 않았다면 기본적으로 사용할 수 있다. 다른 핸들러 매핑 빈을 등록했을 경우에는 디폴트 핸들러 매핑전략이 적용되지 않으므로 DefaultAnnotationHandlerMapping 도 함께 빈으로 등록해줘야 한다.

클래스/메소드 결합 매핑정보

DefaultAnnotationHandlerMapping 의 핵심은 매핑정보로 @RequestMapping 애노테이션을 활용한다는 점이다. 그런데 @RequestMapping 은 타입 레벨뿐 아니라 메소드 레벨도 붙일 수 있다. 스프링은 이 두 가지 위치에 붙은 @RequestMapping 의 정보를 결합해서 최종 매핑정보를 생성한다. 기본적인 결합 방법은 타입 레벨의 @RequestMapping 정보를 기준으로 삼고, 메소드 레벨의 @RequestMapping 정보는 타입 레벨의 매핑을 더 세분화하는 데 사용하는 것이다. 타입 레벨의 @RequestMapping 이 메소드 레벨 @RequestMapping 의 공통 정보라고 볼 수 있다.

1. @RequestMapping 애노테이션

@RequestMapping 애노테이션에는 다음과 같은 엘리먼트를 지정할 수 있다. 모든 엘리먼트는 생략 가능하다.

String[] value(): URL 패턴

디폴트 엘리먼트인 value 는 스트링 배열 타입으로 URL 패턴을 지정하도록 되어 있다. 대부분의 핸들러 매핑은 요청정보 중에서 URL 만을 사용한다. URL은 가장 기본이 되는 매핑정보다. URL 에 부가적인 조건을 더해서 세분화된 매핑을 할 수 있다. URL 패턴은 다른 핸들러 매핑에서 사용하는 URL 패턴처럼 ANT 스타일의 와일드 카드를 사용할 수 있다. 예를 들면 다음과 같은 URL 패턴을 사용할 수 있다.

1
2
3
4
@RequestMapping("/hello")
@RequestMapping("/main*")
@RequestMapping("/view.*")
@RequestMapping("/admin/**/user")

와일드카드(*)를 사용한 부분은 타입과 메소드의 매핑정보 결합을 통해 좀 더 구체적으로 만들 수 있다.

@RequestMapping 에는 다음과 같이 {} 를 사용하는 URI 템플릿을 사용할 수도 있다. 이 때 {} 위치에 해당하는 내용을 컨트롤러 메소드에서 파라미터로 전달받을 수 있다. {}에 들어가는 이름을 패스변수(Path Variable)라고 부르며, 하나 이상 등록할 수 있다.

1
@RequestMapping("/user/{userid}")

URL 패턴은 배열이다. 따라서 다음과 같이 하나 이상의 URL 패턴을 지정할 수 있다.

1
@RequestMapping({"/hello", "/hi"})

URL 패턴에서 기억해야 할 중요한 사실은 디폴트 접미어 패턴이 적용된다는 점이다. 예를 들어 다음과 같이 @RequestMapping을 적용했다고 하자.

1
@RequestMapping("/hello")

이렇게 확장자가 붙지 않고 /로 끝나지도 않은 URL 패턴에는 디폴트 접미어 패턴이 적용돼서 실제로는 다음 세개의 URL 패턴을 적용했을 때와 동일한 결과가 나온다.

1
@RequestMapping({"/hello","/hello/","/hello.*"})

따라서 /hello 라고 정의한 것만으로 /hello.do, /hello.html 과 같이 확장자가 붙은 URL 이나, /hello/ 처럼 /로 끝나는 URL도 자동으로 매핑된다.

RequestMethod[] method() : HTTP 요청 메소드

RequestMethod 는 HTTP 메소드를 정의한 이늉이다. GET, HEAD, POST, PUT, DELETE, OPTIONS, TRACE 7개의 HTTP 메소드가 정의되어 있다. @RequestMapping 에 HTTP 요청 메소드를 추가해 주면 같은 URL 이라고 하더라도 요청 메소드에 따라 다른 메소드를 매핑해줄 수 있다. 예를 들어 다음과 같이 GET 과 POST 를 구분할 수 있다.

1
2
@RequestMapping(value="/user/add", method=RequestMethod.GET)
@RequestMapping(value="/user/add", method=RequestMethod.POST)

HTTP 요청 메소드도 배열타입이므로 하나 이상을 지정할 수 있다. 때로는 타입 레벨에는 URL 만 주고 메소드 레벨에는 HTTP 요청 메소드만 지정하는 방법을 사용하기도 한다. 이럴 때는 다음과 같이 URL을 생략할 수도 있다.

1
@RequestMapping(method=RequestMethod.GET)

String[] params(): 요청 파라미터

같은 URL을 사용하더라도 HTTP 요청 파라미터에 따라 별도의 작업을 해주고 싶을 때가 있다. 이 때는 컨트롤러 코드에서 파라미터를 검사해서 기능을 분리하는 대신 @RequestMapping에 매핑을 위한 요청 파라미터를 지정해줄 수 있다. 파라미터는 '타입=값' 형식으로 매핑해주면 된다.

다음 두 가지 매핑 선언을 살펴보자

1
2
@RequestMapping(value="/user/edit", params="type=admin")
@RequestMapping(value="/user/edit", params="type=member")

두 개의 매핑 모두 /user/edit 이라는 URL 을 갖고 있지만 파라미터가 다르다. /user/edit?type=admin 이라는 URL로 요청을 받으면 첫 번째 매핑이 적용되고, /user/edit?type=member 라는 URL 이 들어 오면 두 번째 매핑이 적용된다.

여기에 다음과 같이 파라미터 없이 URL 만 적용된 @RequestMapping 이 하나 더 추가되어 있다고 생각해 보자.

1
@RequestMapping("/user/edit")

이 때 /user/edit?type=admin 이라는 URL로 요청을 보내면 어떤 매핑이 적용될 까? 매핑 기준으로 보자면 위의 첫 번째 매핑과 마지막에 추가한 URL 만 있는 매핑 조건을 동시에 만족한다. 이럴 때는 구분이 좀 더 상세한 쪽이 선택된다. 따라서 첫 번째 매핑에 해당된다. 이 원리는 모든 경우에 다 동일하게 적용된다. 매핑조건을 만족하는 경우가 여러 개 있을 때는 좀 더 많은 조건을 만족시키는 쪽이 우선된다. URL 에서도 더 긴 조건을 만족하는 쪽이 우선된다.

이렇게 세 개의 @RequestMapping 선언을 했을 경우에 type=admin, type=member 와 같은 파라미터를 갖지 않으면서 URL 이 /user/edit 인 요청은 모두 세번 째 @RequestMapping 으로 매핑된다. 이렇게 디폴트 매핑을 만들고 특별한 조건을 가진 경우만 별도의 매핑을 추가할 수가 있다.

params 에 지정한 파라미터는 꼭 URL 에 포함된 것만 비교하는 건 아니다. 폼에서 POST 로 전송한 폼 파라미터도 비교 대상이다.

특정 파라미터가 존재하지 않아야 한다는 조건을 지정할 수도 있다. 이때는 "!" 를 파라미터 이름 앞에 붙여주면 된다. 다음 선언은 type 이라는 파라미터가 아예 존재하지 않는 경우에만 매핑되도록 선언한 것이다.

1
@RequestMapping(value="/user/edit", params="!type")

params 도 배열도 선언되어 있으므로 하나 이상을 지정할 수 있다.

String[] headers : HTTP 헤더

마지막 매핑 항목은 HTTP 헤더 정보다. 지정 방식은 params 와 비슷하게 '헤더이름=값' 형식을 사용한다. 다음 매핑 선언은 헤더의 content-type 이 text/html, text/plain 등으로 되어 있는 경우에만 매핑해준다.

1
@RequestMapping(value="/view", headers="content-type=text/*")

2. 타입 레벨 매핑과 메소드 레벨 매핑의 결합

타입 레벨에 붙는 @RequestMapping 은 타입 내의 모든 매핑용 메소드의 공통 조건을 지정할 때 사용한다. 그리고 메소드 레벨에서 조건을 세분화해주면 된다. 메소드 레벨의 매핑은 타입 레벨의 매핑을 상속받는다고 보면 된다.

예를 들어 컨트롤러 메소드가 각각 /user/add, /user/edit, /user/delete 라는 URL에 매핑돼야 한다고 해보자. 이 때는 아래와 같이 타입레벨에는 세 URL 의 공통 부분인 /user 까지만 매핑해 주고 메소드 레벨에서 나머지 /add, /edit, /delete 를 선언해 주면 이 두가지 조건이 결합돼서 최종 URL 매핑 조건이 결정된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
@RequestMapping("/user")
public class UserController {
     
    @RequestMapping("/add")
    public String add(...) {}
     
    @RequestMapping("/edit")
    public String edit(...) {}
     
    @RequestMapping("/delete")
    public String delete(...) {}
 
}

타입 레벨의 URL 패턴에 * 나 ** 를 사용했을 때도 URL을 결합할 수 있다. 타입 레벨에 /user 대신 /user/* 를 사용했을 경우 메소드 레벨에 /add 가 선언되어 있으면 /user/add 로 결합된다. 타입레벨에 /user/** 로 되어 있다면 메소드 레벨의 /add 는 /user/**/add 로 결합된다. 

메소드 레벨의 시작 /를 생략할 수도 있다. 타입 레벨이 /user, /user/, /user/* 중 하나로 되어 있는 경우 메소드 레벨에 add 라는 URL 조건을 주면 모두 /user/add 로 매핑된다.

타입레벨과 메소드 레벨의 URL을 결합하는 대신 다음과 같이 URL은 타입레벨에서만 정의하고 메소드 레벨에서는 다른 매핑조건을 추가해 줄 수도 있다. 

1
2
3
4
5
6
7
8
9
10
@RequestMapping("/user/add")
public class UserController {
     
    @RequestMapping(method=RequestMethod.GET)
    public String form(...) {}
     
    @RequestMapping(method=RequestMethod.POST)
    public String submit(...) {}
 
}

3. 메소드레벨 단독 매핑

메소드 레벨의 매핑조건에 공통점이 없는 경우라면 타입 레벨에서는 조건을 주지 않고 메소드 레벨에서 독립적으로 매핑정보를 지정할 수도 있다. 이때 타입 레벨에는 조건이 없는 @RequestMapping을 붙여 두면 된다. 이마저 생략하면 아예 클래스 자체가 매핑 대상이 되지 않으니 내용이 없는 빈 @RequestMapping 이라도 꼭 부여해줘야 한다.

이방법을 이용하면 다음과 같이 메소드마다 다른 URL 로 선언할 수가 있다.

1
2
3
4
5
6
7
8
9
10
@RequestMapping
public class UserController {
     
    @RequestMapping("/hello")
    public String hello(...) {}
     
    @RequestMapping("/main)
    public String main(...) {}
     
}

컨트롤러 클래스에 @Controller 애노테이션을 붙여서 빈 자동스캔 방식으로 등록되게 했다면, 이때는 클래스 레벨의 @RequestMapping 을 생략할 수도 있다. 스프링이 @Controller 애노테이션을 보고 애노테이션 방식을 사용한 클래스라고 판단하기 때문이다. 클래스 레벨에 매핑조건을 주지 않는다면 아래와 같이 @RequestMapping을 생략할 수 있다.

1
2
3
4
5
6
7
8
9
10
@Controller
public class UserController {
     
    @RequestMapping("/hello")
    public String hello(...) {}
     
    @RequestMapping("/main)
    public String main(...) {}
     
}

4. 타입 레벨 단독 매핑

핸들러 매핑은 원래 핸들어 오브젝트를 결정하는 전략이다. 애노테이션의 영향으로 매핑 방법이 메소드 레벨까지 세분화되기는 했지만 다른 타입 컨트롤러와의 일관성을 위해 애노테이션 방식의 핸들러 매핑에서도 일단 오브젝트까지만 매핑하고, 최종 실행할 메소드는 핸들러 어댑터가 선정한다.

그렇기 때문에 @RequestMapping 을 타입 레벨에 단독으로 사용해서 다른 타입 컨트롤러를 위한 매핑을 위해 사용할 수도 있다. 예를 들면 아래와 같이 Controller 인터페이스를 구현한 컨트롤러 클래스에 @RequestMapping 을 붙여줄 수 있다. 원칙적으로 핸들로 매핑과 핸들러 어댑터는 독립적으로 조합될 수 있기 때문에 적용 가능한 방법이다.

1
2
3
4
5
6
@RequestMapping("/hello")
public class HelloController implements Controller {
     
 
     ...
}

다음과 같이 클래스 레벨의 URL 패턴이 /* 로 끝나는 경우에는 메소드 레벨의 URL 패턴으로 메소드 이름이 사용되게 할 수 있다. 아래와 같이 클래스 레벨에서는 /* 로 끝나게 하고 메소드 레벨에서는 빈 @RequestMapping 애노테이션만 부여해주면 메소드 이름이 URL 대신 적용돼서 각각 /user/add 와 /user/edit 으로 매핑된다.

1
2
3
4
5
6
7
8
9
@RequestMapping("/user/*")
public class UserController  {
     
     @RequestMapping
     public String add(...) {}
      
     @RequestMapping
     public String edit(...) {}
}

타입 상속과 매핑

@RequestMapping 이 적용된 클래스를 상속해서 컨트롤러로 사용하는 경우 슈퍼클래스의 매핑정보는 어떻게 될까? 상속과 관련해서는 기본 원칙 두 가지만 기억하고 있으면 된다. @RequestMapping 정보는 상속된다. 단, 서브 클래스에서 @RequestMapping 을 재정의하면 슈퍼클래스의 정보는 무시된다.

지금까지 타입 레벨의 @RequestMapping 을 설명하면서 클래스의 경우만 예로 들었다. 타입과 메소드 사이의 기본적인 원칙은 인터페이스에도 동일하게 적용된다. 인터페이스의 @RequestMapping 은 인터페이스를 구현한 클래스의 매핑정보로 사용된다. 같은 인터페이스 안에서 타입 레벨과 메소드 레벨 사이의 관계와 매핑조건 결합은 지금까지 설명한 내용이 모두 적용된다.

컨트롤러 클래스를 인터페이스를 구현해서 만드는 경우는 많지 않다. 특정 인터페이스의 구현조차 강요하지 않는 애노테이션 방식을 사용하면서 굳이 인터페이스를 사용할 이유는 없다. 컨트롤러를 다른 빈이 DI를 통해 참조하는 경우도 거의 없다. 하지만 스프링 MVC 를 기반으로 확장 MVC 프레임워크를 설계한다면 얼마든지 의미 있는 인터페이스를 적용할 수 있다. 이 경우 @RequestMapping 을 인터페이스 레벨에 부여한다면, 타입 상속(즉, 인터페이스 구현)를 통해 공통 매핑정보를 일괄적으로 적용할 수 있다.

인터페이스 구현에 의한 @RequestMapping 정보 상속은 클래스 상속과 거의 비슷하지만 몇 가지 차이점이 있으니 추의해야 한다.

1. 매핑정보 상속의 종류

상위 타입과 메소드의 @RequestMapping 상속

슈퍼클래스에만 @RequestMapping 을 적용하고 이를 그대로 상속한 서브 클래스에는 아무런 @RequestMapping 을 사용하지 않았을 경우를 생각해 보자.

클래스 상속의 경우는 단순하다. 슈퍼클래스의 모든 매핑정보를 그대로 서브클래스가 물려받는다. 아래 예를 살펴보자.

1
2
3
4
5
6
7
8
9
10
11
@RequestMapping("/user")
public class Super  {
     
     @RequestMapping("/list")
     public String list(...) {}
      
}
 
public class Sub extends Super {
     
}

매핑정보는 Super 클래스에만 있고 Sub 클래스에는 없다. Sub 클래스는 Super 이 list() 메소드를 상속받는다. 동시에 Super 클래스와 list() 메소드에 붙은 @RequestMapping 정보도 그대로 상속받는다. Sub 클래스를 컨트롤러로 등록한다면, /user/list URL은 list() 메소드로 매핑된다.

이번에는 아래와 같이 메소드를 오버라이드한 경우를 생각해 보자. 서브 클래스에서 메소드를 오버라이드했더라도 메소드에 @RequestMapping을 붙이지 않는다면 슈퍼클래스 메소드의 매핑정보는 그대로 상속된다. 따라서 Sub 클래스의 list() 메소드는 여전히 /user/list 에 매핑된다.

1
2
3
4
public class Sub extends Super {
    public String list() { ... }
     
}

인터페이싀 경우도 마찬가지다. 인터페이스 타입과 메소드 레벨에 정의한 @RequestMapping 의 매핑정보는 구현 클래스에 그대로 상속된다.

상위 타입의 @RequestMapping 과 하위 타입 메소드의 @RequestMapping 결합

슈퍼클래스에는 타입에만 @RequestMapping 이 선언되어 있고, 서브 클래스에는 타입레벨에는 아무 매핑정보가 없고 메소드에만 @RequestMapping 이 있는 경우를 생각해 보자. 이 때도 슈퍼클래스의 타입 레벨 @RequestMapping 이 상속된다는 원칙이 적용된다. 결국 서브 클래스의 타입 레벨에 @RequestMapping 이 정의되어 있는 것처럼 각 메소드의 매핑정보가 결합돼서 최종적인 메소드 레벨의 매핑조건을 만들어 낸다.

상위 타입 메소드의 @RequestMapping 과 하위 타입의 @RequestMapping 결합

앞의 경우와 반대되는 것이다. 슈퍼클래스에는 메소드에만 @RequestMapping 이 있고, 서브 클래스에는 반대로 클래스 레벨에 @RequestMapping 이 부여된 경우다. 이 때도 마찬가지로 @RequestMapping 정보가 그대로 상속된 후에 결합된다.

인터페이스의 경우도 동일한 방식으로 적용된다. 단, 인터페이스를 구현하는 메소드에 URL 이 없는 빈 @RequestMapping 을 붙이면 인터페이스 메소드의 매핑정보가 무시되니 주의해야 한다.

하위 타입과 메소드의 @RequestMapping 재정의

상속 또는 구현을 통해 만들어진 하위 타입에 @RequestMapping 을 부여하면 상위 타입에서 지정한 @RequestMapping 매핑정보를 대체해서 적용된다.

인터페이스도 동일하다.

서브클래스 메소드의 URL 패턴 없는 @RequestMapping 재정의

하위 타입의 @RequestMapping 은 항상 상위 타입의 @RequestMapping 정보를 대신한다. 그런데 희한하게도 클래스 상속에서 오버라이드한 하위 메소드에 한해서는 URL 조건이 없는 @RequestMapping 을 붙였을 경우에 상위 메소드의 @RequestMapping 의 URL 조건이 그대로 상속된다. 버그일 수 있으므로 하위 타입의 메소드에 @RequestMapping 을 붙일 때는 항상 매핑조건을 지정해서 상위 메소드의 매핑 정보를 대체하도록 만들어야 한다고 기억해두자.


@RequestMapping 정보가 클래스 상속이나 인터페이스 구현을 통해 상속된다는 사실을 잘 활용하면 매우 편리한 매핑 전략을 만들어낼 수도 있다. 타입만 바꿀 수 있는 제너릭스 인터페이스를 사용하거나 추상 클래스에 매핑정보를 미리 넣어두고 이를 상속하는 개별 컨트롤러 클래스에서 필요한 부가 매핑정보만 넣도록 만들면, 컨트롤러마다 들어가는 공통적인 매핑 작업을 대폭 줄일 수 있다. 

반면에 체계적인 정책과 잘 준비된 가이드라인을 가지고 개발하지 않고 개발자마다 제멋대로 @RequestMapping 을 남발해서 적용하면 이해하고 관리하기 매우 힘든 코드를 낳게 될 테니 주의해야 한다. 자유도가 높은 애노테이션 방식의 @MVC 를 사용한다면 반드시 강력한 표준 정책을 만들어둘 것을 권장한다.

2. 제네릭스와 매핑정보 상속을 이용한 컨트롤러 작성

@RequestMapping 을 상속과 구현을 통해 잘 활용하면 반복적인 설정을 피하고 간결한 코드를 얻어 낼 수 있다. 특히 자바 5 이상의 타입 파라미터를 이용한 제네릭스를 활용해서 상위 타입에는 타입 파라미터와 메소드 레벨의 공통 매핑정보를 지정해 놓고, 이를 상속받는 개발 컨트롤러에는 구체적인 타입과 클래스 레벨의 기준 매핑정보를 지정해 주는 기법을 사용할 수 있다.

기본 정보의 입출력을 다루는 컨트롤러에는 도메인 오브젝트별로 CRUD 와 검색 기능을 가진 메소드가 중복돼서 등장한다. 값의 검증이나 뷰 로직 등은 컨트롤러에서 독립적으로 만들 수 있으므로, 이런 컨트롤러들은 서비스 계층의 CRUD 메소드로 요청을 위임해주는 것이 전부인 경우도 적지 않다. 각 컨트롤러마다 모델의 타입만 달라질 뿐 기본적인 구성과 코드는 동일한 코드가 중복돼서 만들어지기 마련이다. CRUD 용 컨트롤러라면 모델은 보통 도메인 오브젝트를 사용할 것이다.

타입만 달라지는 중복된 코드라면 제네릭스 타입 파라미터를 가진 슈퍼클래스로 공통적인 코드를 뽑아내는 것이 좋다. 동시에 매핑정보의 일부, 즉 URL 일부가 증복되는 것도 슈퍼클래스에 미리 정의해 둘 수 있다.

User 컨트롤러가 아래와 같이 정의되어 있다고 생각해보자. 일단은 컨트롤러 메소드의 파라메터에 어떻게 User 가 들어갈 수 있는지, 리턴 값의 타입은 어떻게 선택한 것인지는 신경쓰지 말자. 아무튼 이런 식의 컨트롤러 구성은 도메인 오브젝트(여기서는 User)만 바뀐 채로 여러 컨트롤러에서 반복될 것이다. 코드의 내용도 대부분 비슷하다. UserService 의 add(User) 메소드를 호출하는 식의 위임코드가 전부인 경우가 대부분이다. 컨트롤러의 역할 중에서 파라미터 파싱, 요청 정보 검증, 뷰 선택 로직 등은 모두 컨트롤러 밖으로 분산시킬 수 있기 때문이다. 그래서 이런 식의 CRUD 용 컨트롤러는 타입 파라미터를 가진 제네릭스 추상 클래스를 만들어 활용하기에 적합한 후보다.

1
2
3
4
5
6
7
8
9
10
11
public class UserController {
     
    private UserService service;
     
    public void add(User user) { ... }
    public void update(User user) { ... }
    public User view(Integer id) { ... }
    public void delete(Integer id) { ... }
    public List<User> list() { ... }
     
}

UserController 에 적용할 수 있는 제네릭 추상 클래스를 만들어 보자. 아래 코드는 모든 CRUD 컨트롤러에서 상속받을 수 있게 만들어진 제네릭 추상 클래스다.

1
2
3
4
5
6
7
8
9
10
11
public abstarct class GenericController<T, K, S> {
     
    protected S service;
     
    public void add(T entity) { ... }
    public void update(T entity) { ... }
    public T view(K id) { ... }
    public void delete(K id) { ... }
    public List<T> list() { ... }
     
}

개별 컨트롤러는 GenericController 클래스를 상속해서 만들면 된다. 아래는 GenericController 를 이용해 UserController 를 만든 것이다.

1
2
3
4
5
public class UserController extends GenericController<User, Integer, UserService> {
     
     
     
}

그런데 아직 UserController 에는 매핑정보가 없다. 메소드 레벨의 컨트롤러를 정의하고 있으니 @RequestMapping 을 이용한 URL 매핑이 필요할 것이다.

일단 URL을 작성하는 일관된 패턴이 있어야 한다. 도메인 오브젝트 이름을 먼저 넣고 다음에 메소드 이름이 들어가는 식이라면 /user/add, /user/update, /user/view 처럼 매핑 URL을 정의할 수 있다.

그렇다면 이 매핑정보는 어디에, 어떻게 넣어야 할까? 바로 이런 경우에 @RequestMapping 상속을 활용하면 된다. GenericController 에는 도메인 오브젝트가 바뀌더라도 변하지 않는 메소드 레벨의 매핑정보를 다음과 같이 넣는다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class GenericController<T, K, S> {
     
    protected S service;
     
    @RequestMapping("/add")
    public void add(T entity) { ... }
     
    @RequestMapping("/update")
    public void update(T entity) { ... }
     
    @RequestMapping("/view")
    public T view(K id) { ... }
     
    @RequestMapping("/delete")
    public void delete(K id) { ... }
     
    @RequestMapping("/list")
    public List<T> list() { ... }
     
}

그리고 이를 상속한 UserController 에는 아래와 같이 클래스 레벨에 @RequestMapping 을 부여하면 URL 매핑과 컨트롤러 로직이 모두 적용된 컨트롤러를 완성할 수 있다. 물론 login() 처럼 개별 컨트롤러에 추가한 컨트롤러 메소드에는 직접 매핑정보를 넣어줘야 한다.

1
2
3
4
5
6
7
@RequestMapping("/user")
public class UserController extends GenericController<User, Integer, UserService> {
     
    @RequestMapping("/login")
    public String login(String userId, String passsword) { ... }
     
}

상위 타입에선 메소드에, 하위 타입에선 클래스에 @RequestMapping 을 붙여서 이를 결합시키는 매핑정보 상속 방법을 응용한 것이다. 

제네릭을 적용한 컨트롤러를 본격적으로 사용할 생각이라면 비슷한 방법을 서비스 계층과 DAO까지 확장해서 제네릭 DAO, 제네릭 서비스도 함께 적용하는 것이 편리하다. 스프링을 기반으로 제네릭스를 모든 계층에 적용해서 고속 개발이 가능하도록 만든 프레임워크를 살펴보고 싶다면 OSAF (http://whiteship.me/category/OSAF)의 소스코드를 참고해보기 바란다. 스프링 2.5 를 기반으로 만들어지긴 했지만, @RequestMapping 매핑을 사용하는 컨트롤러에 제네릭스를 적용하는 방법에 대해 많은 아이디어를 얻을 수 있을 것이다.


출처 - http://springsource.tistory.com/11






'Framework & Platform > Spring' 카테고리의 다른 글

Sping MVC - Model  (0) 2012.05.09
Spring - @Controller  (0) 2012.05.09
Spring MVC Deprecated API  (0) 2012.05.08
Spring MVC - MultiActionController 사용하기  (0) 2012.05.08
spring controller 종류  (0) 2012.05.08
Posted by linuxism
,

Deprecated API


Contents
Deprecated Interfaces
org.springframework.core.ConcurrentMap 
          as of Spring 3.0, since standard ConcurrentMap is available on Java 5+ anyway 
org.springframework.core.enums.LabeledEnum 
          as of Spring 3.0, in favor of Java 5 enums. 
org.springframework.core.enums.LabeledEnumResolver 
          as of Spring 3.0, in favor of Java 5 enums. 
 

Deprecated Classes
org.springframework.test.annotation.AbstractAnnotationAwareTransactionalTests 
          as of Spring 3.0, in favor of using the listener-based test context framework (AbstractJUnit38SpringContextTests) 
org.springframework.test.jpa.AbstractAspectjJpaTests 
          as of Spring 3.0, in favor of using the listener-based test context framework (AbstractJUnit38SpringContextTests) 
org.springframework.core.enums.AbstractCachingLabeledEnumResolver 
          as of Spring 3.0, in favor of Java 5 enums. 
org.springframework.web.portlet.mvc.AbstractCommandController 
          as of Spring 3.0, in favor of annotated controllers 
org.springframework.web.servlet.mvc.AbstractCommandController 
          as of Spring 3.0, in favor of annotated controllers 
org.springframework.test.AbstractDependencyInjectionSpringContextTests 
          as of Spring 3.0, in favor of using the listener-based test context framework (AbstractJUnit38SpringContextTests) 
org.springframework.web.portlet.mvc.AbstractFormController 
          as of Spring 3.0, in favor of annotated controllers 
org.springframework.web.servlet.mvc.AbstractFormController 
          as of Spring 3.0, in favor of annotated controllers 
org.springframework.core.enums.AbstractGenericLabeledEnum 
          as of Spring 3.0, in favor of Java 5 enums. 
org.springframework.test.jpa.AbstractJpaTests 
          as of Spring 3.0, in favor of using the listener-based test context framework (AbstractJUnit38SpringContextTests) 
org.springframework.core.enums.AbstractLabeledEnum 
          as of Spring 3.0, in favor of Java 5 enums. 
org.springframework.test.web.AbstractModelAndViewTests 
          as of Spring 3.0, in favor of using the listener-based test context framework (AbstractJUnit38SpringContextTests) or ModelAndViewAssert with JUnit 4 and TestNG. 
org.springframework.test.AbstractSingleSpringContextTests 
          as of Spring 3.0, in favor of using the listener-based test context framework (AbstractJUnit38SpringContextTests) 
org.springframework.test.AbstractSpringContextTests 
          as of Spring 3.0, in favor of using the listener-based test context framework (AbstractJUnit38SpringContextTests) 
org.springframework.test.AbstractTransactionalDataSourceSpringContextTests 
          as of Spring 3.0, in favor of using the listener-based test context framework (AbstractJUnit38SpringContextTests) 
org.springframework.test.AbstractTransactionalSpringContextTests 
          as of Spring 3.0, in favor of using the listener-based test context framework (AbstractJUnit38SpringContextTests) 
org.springframework.web.portlet.mvc.AbstractWizardFormController 
          as of Spring 3.0, in favor of annotated controllers 
org.springframework.web.servlet.mvc.AbstractWizardFormController 
          as of Spring 3.0, in favor of annotated controllers 
org.springframework.web.servlet.view.xslt.AbstractXsltView 
          since Spring 2.5; superseded by XsltView and its more flexible XsltView.locateSource(java.util.Map) mechanism 
org.springframework.test.AssertThrows 
          favor use of JUnit 4's @Test(expected=...) support 
org.springframework.web.portlet.mvc.BaseCommandController 
          as of Spring 3.0, in favor of annotated controllers 
org.springframework.web.servlet.mvc.BaseCommandController 
          as of Spring 3.0, in favor of annotated controllers 
org.springframework.web.servlet.mvc.CancellableFormController 
          as of Spring 3.0, in favor of annotated controllers 
org.springframework.test.ConditionalTestCase 
          as of Spring 3.0, in favor of using the listener-based test context framework (AbstractJUnit38SpringContextTests) 
org.springframework.jms.listener.DefaultMessageListenerContainer102 
          as of Spring 3.0, in favor of the JMS 1.1 based DefaultMessageListenerContainer 
org.springframework.jms.core.JmsTemplate102 
          as of Spring 3.0, in favor of the JMS 1.1 based JmsTemplate 
org.springframework.jms.connection.JmsTransactionManager102 
          as of Spring 3.0, in favor of the JMS 1.1 based JmsTransactionManager 
org.springframework.core.enums.LetterCodedLabeledEnum 
          as of Spring 3.0, in favor of Java 5 enums. 
org.springframework.jms.listener.adapter.MessageListenerAdapter102 
          as of Spring 3.0, in favor of the JMS 1.1 based MessageListenerAdapter 
org.springframework.web.context.support.ServletContextFactoryBean 
          as of Spring 3.0, since "servletContext" is now available as a default bean in every WebApplicationContext 
org.springframework.core.enums.ShortCodedLabeledEnum 
          as of Spring 3.0, in favor of Java 5 enums. 
org.springframework.web.portlet.mvc.SimpleFormController 
          as of Spring 3.0, in favor of annotated controllers 
org.springframework.web.servlet.mvc.SimpleFormController 
          as of Spring 3.0, in favor of annotated controllers 
org.springframework.jms.support.converter.SimpleMessageConverter102 
          as of Spring 3.0, in favor of the JMS 1.1 based SimpleMessageConverter 
org.springframework.jms.listener.SimpleMessageListenerContainer102 
          as of Spring 3.0, in favor of the JMS 1.1 based SimpleMessageListenerContainer 
org.springframework.jms.connection.SingleConnectionFactory102 
          as of Spring 3.0, in favor of the JMS 1.1 based SingleConnectionFactory 
org.springframework.core.enums.StaticLabeledEnum 
          as of Spring 3.0, in favor of Java 5 enums. 
org.springframework.core.enums.StaticLabeledEnumResolver 
          as of Spring 3.0, in favor of Java 5 enums. 
org.springframework.core.enums.StringCodedLabeledEnum 
          as of Spring 3.0, in favor of Java 5 enums. 
 

Deprecated Annotation Types
org.springframework.test.annotation.NotTransactional 
          as of Spring 3.0, in favor of moving the non-transactional test method to a separate (non-transactional) test class or to a @BeforeTransaction or @AfterTransaction method. 
 

Deprecated Fields
org.springframework.beans.factory.config.AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT 
          as of Spring 3.0: If you are using mixed autowiring strategies, prefer annotation-based autowiring for clearer demarcation of autowiring needs. 
org.springframework.beans.factory.support.AbstractBeanDefinition.AUTOWIRE_AUTODETECT 
          as of Spring 3.0: If you are using mixed autowiring strategies, use annotation-based autowiring for clearer demarcation of autowiring needs. 
org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.COMMAND_NAME_VARIABLE_NAME 
          as of Spring 2.5, in favor of FormTag.MODEL_ATTRIBUTE_VARIABLE_NAME 
 

Deprecated Methods
org.springframework.aop.framework.AdvisedSupport.addAllAdvisors(Advisor[]) 
          as of Spring 3.0, in favor of AdvisedSupport.addAdvisors(org.springframework.aop.Advisor...) 
org.springframework.ui.ModelMap.addAllObjects(Collection) 
          as of Spring 2.5, in favor of ModelMap.addAllAttributes(Collection) 
org.springframework.ui.ModelMap.addAllObjects(Map) 
          as of Spring 2.5, in favor of ModelMap.addAllAttributes(Map) 
org.springframework.beans.factory.support.BeanDefinitionBuilder.addConstructorArg(Object) 
          since Spring 2.5, in favor of BeanDefinitionBuilder.addConstructorArgValue(java.lang.Object) 
org.springframework.ui.ModelMap.addObject(Object) 
          as of Spring 2.5, in favor of ModelMap.addAttribute(Object) 
org.springframework.ui.ModelMap.addObject(String, Object) 
          as of Spring 2.5, in favor of ModelMap.addAttribute(String, Object) 
org.springframework.core.CollectionFactory.createConcurrentMap(int) 
          as of Spring 3.0, for usage on JDK 1.5 or higher 
org.springframework.core.CollectionFactory.createConcurrentMapIfPossible(int) 
          as of Spring 3.0, for usage on JDK 1.5 or higher 
org.springframework.web.context.ContextLoaderListener.createContextLoader() 
          in favor of simply subclassing ContextLoaderListener itself (which extends ContextLoader, as of Spring 3.0) 
org.springframework.core.CollectionFactory.createCopyOnWriteSet() 
          as of Spring 3.0, for usage on JDK 1.5 or higher 
org.springframework.core.CollectionFactory.createIdentityMapIfPossible(int) 
          as of Spring 2.5, for usage on JDK 1.4 or higher 
org.springframework.core.CollectionFactory.createLinkedCaseInsensitiveMapIfPossible(int) 
          as of Spring 3.0, for usage on JDK 1.5 or higher 
org.springframework.core.CollectionFactory.createLinkedMapIfPossible(int) 
          as of Spring 2.5, for usage on JDK 1.4 or higher 
org.springframework.core.CollectionFactory.createLinkedSetIfPossible(int) 
          as of Spring 2.5, for usage on JDK 1.4 or higher 
org.springframework.remoting.rmi.RmiClientInterceptorUtils.doInvoke(MethodInvocation, Remote) 
          as of Spring 2.5, in favor of RmiClientInterceptorUtils.invokeRemoteMethod(org.aopalliance.intercept.MethodInvocation, java.lang.Object) 
org.springframework.orm.ibatis.SqlMapClientTemplate.executeWithListResult(SqlMapClientCallback) 
          as of Spring 3.0 - not really needed anymore with generic SqlMapClientTemplate.execute(org.springframework.orm.ibatis.SqlMapClientCallback) method 
org.springframework.orm.ibatis.SqlMapClientTemplate.executeWithMapResult(SqlMapClientCallback) 
          as of Spring 3.0 - not really needed anymore with generic SqlMapClientTemplate.execute(org.springframework.orm.ibatis.SqlMapClientCallback) method 
org.springframework.test.annotation.AbstractAnnotationAwareTransactionalTests.findUniqueProfileValueSourceFromContext(ApplicationContext) 
          Use @ProfileValueSourceConfiguration instead. 
org.springframework.aop.config.AopNamespaceUtils.forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry) 
          since Spring 2.5, in favor of AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry) 
org.springframework.util.ClassUtils.forName(String) 
          as of Spring 3.0, in favor of specifying a ClassLoader explicitly: see ClassUtils.forName(String, ClassLoader) 
org.springframework.web.context.ContextLoaderListener.getContextLoader() 
          in favor of simply subclassing ContextLoaderListener itself (which extends ContextLoader, as of Spring 3.0) 
org.springframework.validation.DataBinder.getErrors() 
          in favor of DataBinder.getBindingResult(). Use the BindException.BindException(BindingResult) constructor to create a BindException instance if still needed. 
org.springframework.remoting.rmi.RmiClientInterceptorUtils.invoke(MethodInvocation, Remote, String) 
          as of Spring 2.5, in favor of RmiClientInterceptorUtils.invokeRemoteMethod(org.aopalliance.intercept.MethodInvocation, java.lang.Object) 
org.springframework.core.JdkVersion.isAtLeastJava14() 
          as of Spring 3.0 which requires Java 1.5+ 
org.springframework.core.JdkVersion.isAtLeastJava15() 
          as of Spring 3.0 which requires Java 1.5+ 
org.springframework.core.JdkVersion.isAtLeastJava16() 
          as of Spring 3.0, in favor of reflective checks for the specific Java 1.6 classes of interest 
org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser.isEligibleAttribute(Attr) 
          in favour of AbstractSimpleBeanDefinitionParser.isEligibleAttribute(org.w3c.dom.Attr, ParserContext) 
org.springframework.util.ClassUtils.isPresent(String) 
          as of Spring 2.5, in favor of ClassUtils.isPresent(String, ClassLoader) 
org.springframework.beans.factory.support.AbstractBeanDefinition.overrideFrom(AbstractBeanDefinition) 
          since Spring 2.5, in favor of AbstractBeanDefinition.overrideFrom(BeanDefinition) 
org.springframework.jdbc.core.simple.SimpleJdbcTemplate.query(String, ParameterizedRowMapper, Map) 
           
org.springframework.jdbc.core.simple.SimpleJdbcOperations.query(String, ParameterizedRowMapper, Map) 
          as of Spring 3.0: Use the method using the newly genericized RowMapper interface instead since the RowMapper and ParameterizedRowMapper interfaces are equivalent now. 
org.springframework.jdbc.core.simple.SimpleJdbcTemplate.query(String, ParameterizedRowMapper, Object...) 
           
org.springframework.jdbc.core.simple.SimpleJdbcOperations.query(String, ParameterizedRowMapper, Object...) 
          as of Spring 3.0: Use the method using the newly genericized RowMapper interface instead since the RowMapper and ParameterizedRowMapper interfaces are equivalent now. 
org.springframework.jdbc.core.simple.SimpleJdbcTemplate.query(String, ParameterizedRowMapper, SqlParameterSource) 
           
org.springframework.jdbc.core.simple.SimpleJdbcOperations.query(String, ParameterizedRowMapper, SqlParameterSource) 
          as of Spring 3.0: Use the method using the newly genericized RowMapper interface instead since the RowMapper and ParameterizedRowMapper interfaces are equivalent now. 
org.springframework.jdbc.core.simple.SimpleJdbcTemplate.queryForObject(String, ParameterizedRowMapper, Map) 
           
org.springframework.jdbc.core.simple.SimpleJdbcOperations.queryForObject(String, ParameterizedRowMapper, Map) 
          as of Spring 3.0: Use the method using the newly genericized RowMapper interface instead since the RowMapper and ParameterizedRowMapper interfaces are equivalent now. 
org.springframework.jdbc.core.simple.SimpleJdbcTemplate.queryForObject(String, ParameterizedRowMapper, Object...) 
           
org.springframework.jdbc.core.simple.SimpleJdbcOperations.queryForObject(String, ParameterizedRowMapper, Object...) 
          as of Spring 3.0: Use the method using the newly genericized RowMapper interface instead since the RowMapper and ParameterizedRowMapper interfaces are equivalent now. 
org.springframework.jdbc.core.simple.SimpleJdbcTemplate.queryForObject(String, ParameterizedRowMapper, SqlParameterSource) 
           
org.springframework.jdbc.core.simple.SimpleJdbcOperations.queryForObject(String, ParameterizedRowMapper, SqlParameterSource) 
          as of Spring 3.0: Use the method using the newly genericized RowMapper interface instead since the RowMapper and ParameterizedRowMapper interfaces are equivalent now. 
org.springframework.aop.config.AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(ParserContext, Object) 
          since Spring 2.5, in favor of AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(ParserContext, Element) and AopConfigUtils.registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry, Object) 
org.springframework.orm.hibernate3.HibernateOperations.saveOrUpdateAll(Collection) 
          as of Spring 2.5, in favor of individual saveOrUpdate or merge usage 
org.springframework.beans.factory.support.BeanDefinitionBuilder.setFactoryBean(String, String) 
          since Spring 2.5, in favor of preparing this on the raw BeanDefinition object 
org.springframework.beans.factory.support.BeanDefinitionBuilder.setResourceDescription(String) 
          since Spring 2.5, in favor of preparing this on the raw BeanDefinition object 
org.springframework.beans.factory.support.BeanDefinitionBuilder.setSingleton(boolean) 
          since Spring 2.5, in favor of BeanDefinitionBuilder.setScope(java.lang.String) 
org.springframework.beans.factory.support.AbstractBeanDefinition.setSingleton(boolean) 
          since Spring 2.5, in favor of AbstractBeanDefinition.setScope(java.lang.String) 
org.springframework.beans.factory.support.BeanDefinitionBuilder.setSource(Object) 
          since Spring 2.5, in favor of preparing this on the raw BeanDefinition object 
org.springframework.ui.freemarker.FreeMarkerConfigurationFactory.setTemplateLoaders(TemplateLoader[]) 
          as of Spring 2.0.1, in favor of the "preTemplateLoaders" and "postTemplateLoaders" properties 
org.springframework.web.servlet.mvc.BaseCommandController.suppressValidation(HttpServletRequest) 
          as of Spring 2.0.4, in favor of the BaseCommandController.suppressValidation(HttpServletRequest, Object) variant 
 

Deprecated Constructors
org.springframework.beans.factory.support.AbstractBeanDefinition(AbstractBeanDefinition) 
          since Spring 2.5, in favor of AbstractBeanDefinition.AbstractBeanDefinition(BeanDefinition) 
org.springframework.jdbc.CannotGetJdbcConnectionException(String, ClassNotFoundException) 
          since Spring 2.5, in favor of throwing an IllegalStateException in case of the driver not being found 
org.springframework.jdbc.datasource.DriverManagerDataSource(String, String, String, String) 
          since Spring 2.5. DriverManagerDataSource is primarily intended for accessing pre-registered JDBC drivers. If you need to register a new driver, consider using SimpleDriverDataSource instead. 
org.springframework.beans.factory.support.RootBeanDefinition(Class, boolean) 
          since Spring 2.5, in favor of AbstractBeanDefinition.setScope(java.lang.String) 
org.springframework.beans.factory.support.RootBeanDefinition(Class, MutablePropertyValues, boolean) 
          since Spring 2.5, in favor of AbstractBeanDefinition.setScope(java.lang.String) 
org.springframework.jdbc.datasource.SingleConnectionDataSource(String, String, String, String, boolean) 
          since Spring 2.5. Driver parameter usage is generally not recommended for a SingleConnectionDataSource. If you insist on using driver parameters directly, set up the Driver class manually before invoking this DataSource. 
 


출처 - http://static.springsource.org/spring/docs/3.0.0.RC1/javadoc-api/deprecated-list.html

'Framework & Platform > Spring' 카테고리의 다른 글

Spring - @Controller  (0) 2012.05.09
Spring - @RequestMapping  (0) 2012.05.09
Spring MVC - MultiActionController 사용하기  (0) 2012.05.08
spring controller 종류  (0) 2012.05.08
spring에서 thread  (0) 2012.05.02
Posted by linuxism
,