단일 테이블 상속 (Single Table Inheritance, STI)

단일 테이블 상속이란 관계형 데이터베이스에서 객체 지향 프로그래밍의 상속이라는 개념을 사용하기 위한 방법이다. 하나의 테이블에 기본 모델을 상속하는 여러 모델들의 데이터를 저장하고, 테이블의 특정 컬럼을 해당 데이터와 연결될 모델을 구분하기 위해 사용한다.

Active Record의 CoC (Convention over Configuration)

Rails(Active Record)에서는 모델이 단일 테이블 상속을 사용하는 경우, type 컬럼을 모델 식별을 위해 사용하는 것이 관례이다.

예를 들어, User 모델을 상속하는 WriterReader 가 있고, 우리가 STI를 사용하려 한다면, User 모델의 마이그레이션은 type 컬럼을 포함해야 한다.

1
2
3
4
5
6
7
8
9
10
class CreateUsers < ActiveRecord::Migration[5.1]
def change
create_table :users do |t|
t.string :email
t.string :password_digest
t.string :type # STI에 사용될 컬럼
t.timestamps
end
end
end

STI 사용하기

이제 User 모델을 상속받는 Writer, Reader 모델을 만들어보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class User < ApplicationRecord
...
end

class Writer < User
has_many :novels
before_destroy :can_leave?, prepend: true

def can_leave?
if novels.count > 0
errors[:base] << '연재중인 소설이 있어서 탈퇴할 수 없습니다.'
throw :abort
end

true
end
end

class Reader < User
has_many :payments
end

둘 다 User의 속성과 함수를 가지지만, Reader 는 탈퇴 조건이 따로 없는 반면, Writer 의 경우 연재중인 소설이 있는 경우에는 탈퇴할 수 없도록 설정되어 있다. WriterReader 모델은 따로 테이블을 가지지 않고, User 테이블에 type: 'Writer' 혹은 type: Reader 로 저장된다.
생성은 User#create 혹은 Reader#create 로 가능하다. User#create 로 생성하는 경우 type 을 명시해야 한다.

1
2
User.create(email: 'mu29@yeoubi.net', password: '1234', type: 'Reader')
Reader.create(email: 'mu29@yeoubi.net', password: '1234')

STI 관련 이슈

이러한 Rails의 STI에 관한 관례를 알지 못하고 type 컬럼을 사용하면 아래와 같은 오류가 발생하게 된다.

1
2
LoadError:
Unable to autoload constant Writer, expected /Users/injung/Github/ssm-api/app/models/writer.rb to define it

type 컬럼은 쓰고 싶지만, STI를 쓰고 싶지 않다면 해당 설정을 비활성화 할 수 있다.

1
2
3
class User < ApplicationRecord
self.inheritance_column = :_type_disabled
end

Comment and share

RESTful API

in backend

소개

REST는 Representational State Transfer의 약자로, 네트워크 어플리케이션을 디자인하기 위한 아키텍쳐 스타일이다. 이것은 장비 간 통신을 위해 CORBA, RPC, 혹은 SOAP등의 복잡한 방법을 사용하는 대신, 간단하게 HTTP를 이용하는 것을 목적으로 한다.
REST는 자원 지향 구조(Resource Oriented Architecture)로 웹 사이트의 이미지, 텍스트, DB 내용 등의 모든 자원에 고유한 URI를 부여한다. 한 가지 알아두어야 할 것은, REST가 표준은 아니라는 것이다. 월드 와이드 웹의 표준을 개발하고 장려하는 W3C는 REST를 사용하라고 하지는 않는다.

CRUD

REST에서 자원에 대한 작업은 HTTP Request를 이용한다. 작업에는 Create(생성), Read(조회), Update(수정), Delete(삭제)가 있고, 이를 줄여서 CRUD라고 한다. 여기에 대응하는 HTTP 메소드는 각각 Post, Get, Put, Delete이다. 실제로 HTTP Reqeust를 보내고 결과를 받는 부분을 살펴보자.

Request

1
2
GET /
Accept: application/userdb

Response

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
200 OK
Content-Type: application/userdb

{
"version": "1.0",
"links": [
{
"href": "/user",
"rel": "list",
"method": "GET"
},
{
"href": "/user",
"rel": "create",
"method": "POST"
}
]
}

조건

REST 아키텍쳐는 다음과 같은 6개의 조건을 가진다.

1. Uniform Interface

REST는 HTTP 표준에만 따른다면, 특정 언어나 기술에 구애받지 않고 사용이 가능한 인터페이스 스타일이다. HTTP의 창시자 중 한 사람인 Roy Fielding은 네 가지의 인터페이스 제약을 제시했다.

  • Resource-Based 의역하자면 리소스 식별 로, 각각의 리소스는 URI를 구분자로 이용하여 구분되어야 한다는 것이다. 예를 들어, http://www.google.com 은 구글의 메인 페이지를 정의한다.
  • Manipulation of Resources Through Representations 표현을 통한 리소스 처리 는 같은 리소스라도 클라이언트에 따라 다르게 보여질 수 있다는 것이다. 뉴스 사이트를 생각해보자. 데스크탑으로 접속할 때와 모바일로 접속할 때 화면은 서로 다르지만 같은 리소스를 쓰고 있다.
  • Self-descriptive Messages 자기 서술적 메시지 란 각 메시지는 자신을 어떻게 처리해야 하는지에 대한 모든 정보를 포함해야 한다는 것이다. 이러한 종류의 메시지는 state-less 혹은 context-free 라고 부르기도 한다.
  • Hypermedia as the Engine of Application State (HATEOAS) 메시지를 보내는 것은 어플리케이션의 상태를 변화시킨다. 예를 들어, 새로운 유저 정보를 서버에 POST하게 되면 유저 리스트를 하나 늘려 어플리케이션의 상태를 변화시킨다. GET을 통해 유저 목록을 요청하면 어플리케이션의 새로운 상태를 받을 수 있다. POST와 GET은 모두 hypermedia link 를 통해 수행되는데(<a href=~>, <form action=~>), 이런 방법을 통해 RESTful 어플리케이션은 하이퍼미디어를 통해 어플리케이션의 상태를 변화시킨다. 이를 어플리케이션 상태 엔진으로서의 하이퍼미디어 라고 한다.

2. Stateless

상태가 없다는 것은 각각의 요청 시에 클라이언트의 컨텍스트가 서버에 저장되지 않는다는 것이다. 클라이언트가 보내는 각각의 요청은 필요한 모든 정보를 담고 있어야 한다. 이렇게 개발하게 되면 서버는 들어오는 요청만을 처리하면 되기 때문에 구현이 단순해진다.

3. Cacheable

WWW에서와 같이, 클라이언트는 응답을 캐싱할 수 있다. 대부분의 서비스에서 CURD중 Read가 가장 빈번하게 호출되기 때문에, 응답을 캐싱하는 것은 서비스의 성능을 향상시킬 수 있다. 클라이언트가 Last-Modified 값을 보냈을 때, 컨텐츠에 변화가 없다면 서버는 304 Not Modified 를 리턴하게 되고 클라이언트에서는 캐시에 저장된 값을 이용하게 된다.

1
2
3
4
5
6
7
8
9
10
GET /partners/UK

200 OK
Last-Modified: Sun, 21 Jan 2007 09:35:19 GMT
[response]

GET /partners/UK
If-Modified-Since: Sun, 21 Jan 2007 09:35:19 GMT

304 Not Modified

4. Client-Server

Uniform Interface는 서버로부터 클라이언트를 분리한다. 클라이언트는 데이터베이스를 신경 쓸 필요가 없고, 사용자 인증이나 컨텍스트를 관리한다. 서버의 경우 유저 인터페이스나 유저 상태를 신경 쓸 필요 없이 API 제공, 데이터 저장, 그리고 로직 처리의 역할만을 담당하게 되어 더 간단해지고 확장이 쉬워진다. 이렇게 역할 구분이 확실해지면 서로의 개발에 있어 의존성이 줄어들게 된다.

5. Layered System

클라이언트는 API 서버만 호출할 뿐, 자신이 대상 서버에 직접 연결되었는지 혹은 중간 서버를 통해 연결되었는지 알 수 없다. 하지만 서버는 다중 계층으로 구성될 수 있다. 서버는 API 서버에 로드밸런싱, 공유 캐시, 암호화 등을 수행하는 계층을 추가해서 유연한 구조로 개발될 수 있다.

6. Code on Demand (optional)

서버는 Java Applet이나 JavaScript와 같이 클라이언트가 실행할 수 있는 코드을 전송하여 클라이언트의 기능을 확장시킬 수 있다. 이것은 REST 아키텍쳐에서 필수는 아니다.

참고

Wikipedia
REST API Tutorial
remotti blog
SLiPP 스터디
조대협의 블로그
일관성 있는 웹 서비스 인터페이스 설계를 위한 REST API 디자인 규칙

Comment and share

  • page 1 of 1

InJung Chung

author.bio


CTO @tenb


Seoul, Korea