Tag Archives: Route

[Ruby] Rails 라우팅

1. 레일즈 라우터의 목적

레일즈 라우터는 외부로부터 들어오는 URL을 인식하고 컨트롤러의 액션으로 보내주는 역할을 합니다. 이를 이용하여 개발중인 뷰에서 하드코딩할 필요 없이 패스와 URL을 생성하는것 역시 가능합니다.

1.1 URL을 코드로 연결하기

당신의 레일즈 어플리케이션이 외부로부터 다음과 같은 요청을 받을 수 있습니다.

GET /patients/17

레일즈는 라우터에게 매칭되는 컨트롤러 액션이 있는지 물어보게 됩니다. 첫번째로 매칭된 라우트 정의는 다음과 같습니다.

get '/patients/:id', to: 'patients#show'

이 요청은 patients 컨트롤러의 show 액션에 { id: ’17’ } 이라는 파라미터와 함께 전달됩니다.

1.2 코드에서 패스와 URL을 생성하기

마찬가지로 패스들과 URL들을 생성하는것도 가능합니다. 위의 라우트 정의를 다음과 같이 바꾸었다고 가정하겠습니다.

get '/patients/:id', to: 'patients#show', as: 'patient'

그리고 당신의 어플리케이션은 컨트롤러의 코드에 다음을 포함합니다.

@patient = Patient.find(17)

그리고 다음의 코드가 컨트롤러에 대응되는 뷰에 존재합니다.

<%= link_to 'Patient Record', patient_path(@patient) %>

위의 과정을 통해 라우터는 /patients/17 이라는 패스를 생성합니다. 이는 당신의 코드의 복잡성을 줄여주고 이해하기 쉽게 만들어줍니다. 라우트 헬퍼를 이용할 때 id가 특정될 필요가 없습니다.

2. 리소스 라우팅 : 레일즈 기본

리소스 라우팅은 index, show, new, edit, create, update, destroy 액션들을 일일이 정의하는것 대신 일반적으로 사용되는 라우트 설정들을 매우 빠르게 정의하는것을 가능케 합니다. 리소스 라우팅은 이러한 정의를 단지 한줄의 코드로 가능하게 해줍니다.

2.1 웹상의 리소스들

브라우저들은 레일즈에 GET, POST, PATCH, PUT, DELETE와 같은 특정 HTTP 메소드를 사용한URL 요청을 통해 페이지를 요청합니다. 각각의 메소드들은 리소스들의 특정 기능을 실행하기 위한 요청이 됩니다. 리소스 라우트는 다양한 요청을 하나의 컨트롤러에 정의된 액션들에 매핑을 해줍니다.

당신의 레일즈 어플리케이션에서 다음과 같은 요청을 받았다고 가정하겠습니다.

DELETE /photos/17

이는 라우터에 질의후 컨트롤러의 액션들에 매핑해주게 됩니다. 첫번째로 매칭된 라우트 정의가 다음과 같을 경우

resources :photos

레일즈는 photos 컨트롤러의 destroy 메소드에 { id: ’17’ } 파라미터와 함께 요청을 전달해 주게 됩니다.

2.2 CRUD (Create, Read, Update, Delete), HTTP메소드, 액션

레일즈에서 리소스 라우트는 HTTP 메소드와 URL을 통해 컨트롤러의 액션에 매핑해 주는 기능을 제공해 줍니다. 컨벤션에의해 각각의 액션은 특정 데이터베이스 CRUD 작업에 매핑됩니다. 라우팅 파일에 다음의 한줄의 설정만으로 구현 가능합니다.

resources :photos

당신의 어플리케이션의 Photos 컨트롤러에 다음과 같은 7개의 다른 라우트 설정이 생성됩니다.

HTTP Method Path Action Used for
GET /photos index 모든 사진을 보여줍니다.
GET /photos/new new 새로운 사진을 등록하기 위한 HTML 폼을 보여줍니다.
POST /photos create 새로운 사진을 생성합니다.
GET /photos/:id show 특정 한개의 사진을 보여줍니다.
GET /photos/:id/edit edit 사진 정보를 수정하기 위한 HTML 폼을 보여줍니다.
PATCH/PUT /photos/:id update 사진 정보를 수정합니다.
DELETE /photos/:id destroy 특정 사진을 삭제합니다.

레일즈 라우트는 정의된 순서에 따라 매치를 하게 됩니다. get ‘photos/poll’ 액션 라우트 설정 위에 :photos 리소스가 정의 되어있을 경우 :photo 리소스에 먼저 매치가 되는 문제가 발생하게 됩니다. 이를 해결하기 위해서는 get 설정을 리소스 정의보다 위에 정의하여 먼저 매치되도록 하여야 합니다.

2.3 패스, URL 헬퍼

리소스 라우트를 생성하면 당신의 어플리케이션의 컨트롤러에 몇개의 헬퍼를 사용가능하게 됩니다. resource :photos 정의가 되어있다고 가정하겠습니다.

  • photos_path 는 /photos 를 반환합니다.
  • new_photo_path 는 /photos/new 를 반환합니다.
  • edit_photo_path(:id) 는 /photos/:id/edit 를 반환합니다. (edit_photo_path(10) 의 경우 /photos/10/edit 를 반환)
  • photo_path(:id) 는 /photos/:id 를 반환 (photo_path(10) 는 /photos/10 을 반환)

이러한 헬퍼들에 대응되는 _url 헬퍼들도 존재합니다. (예: photos_url) URL 헬퍼의 경우 패스 헬퍼와 조금 다르게 현재 호스트, 포트와 패스의 prefix를 포함한 값을 반환합니다.

2.4 다수의 리소스를 한번에 정의하기

한개 이상의 리소스를 생성할 필요가 있을 경우 한번의 resource: 호출로 해결할 수 있습니다.

resources :photos, :books, :videos

위의 정의는 정확하게 다음과 동일한 작업을 수행합니다.

resources :photos
resources :books
resources :videos

2.5 단일 리소스

때때로 클라이언트가 참조할 ID 없이도 접근 가능한 리소스가 필요할 수 있습니다. 예를 들면 /profile 에 접속하면 항상 현재 접속한 유저의 프로필을 보여줄 필요가 있을 경우가 있습니다. 이런 경우 단일 리소스 정의를 사용하여 /profile/:id 대신에 /profile 에 show 액션을 호출하도록 할 수 있습니다.

get 'profile', to: 'users#show'

문자열을 통한 매칭의 경우 Controller#Action 의 형태를 띄지만 심볼을 이용하여 정의하면 액션에 직접적으로 넘겨주게 됩니다.

resource :users do
  get 'profile', to: :show
end

# 또는 다음의 방법도 가능
get 'profile', to: :show, controller: 'users'

다음은 단일 리소스 설정 방법입니다. (단수로 지정하면 됨)

resource :geocoder

당신의 어플리케이션의 Geocoders 컨트롤러(복수형임)에 다음과 같은 6개의 라우팅 설정이 생성됩니다.

HTTP METHOD Path Action Used for
GET /geocoder/new new Geocoder를 생성하기 위한 HTML 폼을 반환합니다.
POST /geocoder create Geocoder를 생성합니다.
GET /geocoder show 하나뿐인 Geocoder 정보를 반환합니다.
GET /geocoder/edit edit Geocoder 값을 수정하는 HTML 폼을 반환합니다.
PATCH/PUT /geocoder update 하나뿐인 Geocoder 값을 수정합니다.
DELETE /geocoder destroy Geocoder 정보를 수정합니다.

단일 리소스 라우트는 다음과 같은 헬퍼들을 만들어 줍니다.

  • new_geocoder_path 는 /geocoder/new 를 반환합니다.
  • edit_geocoder_path 는 /geocoder/edit 를 반환합니다.
  • geocoder_path 는 /geocoder 를 반환합니다.

복수 리소스처럼 호스트, 포트, 패스정보가 포함되는 _url로 끝나는 헬퍼 역시 제공됩니다.

2.6 컨트롤러 네임스페이스와 라우팅

당신은 네임스페이스 이하에 컨트롤러들을 그룹으로 묶어 제공하고 싶을 수 있습니다. 대부분의 경우 관리 기능의 컨트롤러들을 Admin:: 네임스페이스 하에 둘 수 있습니다. 당신은 이런 컨트롤러들을 app/controllers/admin 이하에 둘 수 있고 라우터 설정에서 그룹으로 묶어서 정의할 수 있습니다.

namespace :admin do
  resources :posts, :comments
end

이 정의는 PostsController와 CommentsController에 대해 필요한 라우트를 생성합니다. Admin::PostsController의 경우 레일즈는 다음을 생성합니다.

HTTP METHOD Path Action Used for
GET /admin/posts index admin_posts_path
GET /admin/posts/new new new_admin_post_path
POST /admin/posts create admin_posts_path
GET /admin/posts/:id show admin_post_path(:id)
GET /admin/posts/:id/edit edit edit_admin_post_path(:id)
PATCH/PUT /admin/posts/:id update admin_post_path(:id)
DELETE /admin/posts/:id destroy admin_post_path(:id)

만약 앞에 붙은 /admin 없이 /posts를 Admin::PostsController로 라우팅하고 싶다면 다음과 같이 할 수 있습니다.

scope module: 'admin' do
  resources :posts, :comments
end

또는 다음의 한줄로도 가능합니다.

resources :posts, module: 'admin'

/admin/posts를 Admin:: 모듈 프리픽스 없이 PostsController에 라우팅하고 싶다면 다음의 방법을 사용할 수 있습니다.

scope '/admin' do
  resources :posts, :comments
end

또는 다음의 한줄 선언으로도 가능합니다.

resources :posts, path: '/admin/posts'

위에서 설명한 각각의 방법들 모두 scope를 사용하지 않고도 같은 효과를 내게 됩니다. 마지막 방법의 경우 PostsController에 매핑됩니다.

2.7 중첩 리소스

이 방법은 다른 리소스에 논리적인 자식으로 리소스를 가지는 방법을 말합니다. 예를 들어 당신의 어플리케이션이 다음의 모델들을 가지고 있다고 가정하겠습니다. (하나의 잡지에 다수의 광고가 붙을 수 있는 상황을 가정)

class Magazine < ActiveRecord::Base
  has_many :ads
end

class Ad < ActiveRecord::Base
  belongs_to :magazine
end

중첩 라우트는 이러한 관계들을 알 수 있게 해줍니다. 이런 경우 라우트 선언은 다음과 같습니다.

resources :magazines do
  resources :ads
end

매거진에 라우트를 추가하는 방법으로 이 선언은 ads로의 요청을 AdsController로 라우팅합니다. magazine에 종속된 ad URL들은 다음과 같습니다.

HTTP METHOD Path Action Used for
GET /magazines/:magazine_id/ads index 특정 매거진에 종속된 모든 광고를 보여줍니다.
GET /magazines/:magazine_id/ads/new new 특정 매거진에 종속된 새로운 광고를 추가하는 HTML 폼을 보여줍니다.
POST /magazines/:magazine_id/ads create 특정 매거진에 종속된 새로운 광고를 추가합니다.
GET /magazines/:magazine_id/ads/:id show 특정 매거진에 종속된 특정 광고를 보여줍니다.
GET /magazines/:magazine_id/ads/:id/edit edit 특정 매거진에 종속된 특정 광고의 정보를 수정하는 HTML 폼을 반환합니다.
PATCH/PUT /magazines/:magazine_id/ads/:id update 특정 매거진에 종속된 특정 광고의 정보를 수정합니다.
DELETE /magazines/:magazine_id/ads/:id destroy 특정 매거진에 종속된 특정 광고를 삭제합니다.

이것들은 magazine_ads_url 또는 edit_magazine_ad_path 와 같은 라우트 헬퍼를 생성합니다. 이러한 헬퍼들은 매거진 인스턴스를 첫번째 인자로 받습니다. (magazine_ads_url(@magazine))

2.7.1 중첩 제한

당신은 중첩 리소스를 다른 중첩된 리소스와 함께 사용할 수 있습니다. 예를 들면 다음과 같습니다.

resources :publishers do
  resources :magazines do
    resources :photos
  end
end

깊은 중첩 리소스의 경우 주소가 길어지고 복잡해 질 수 있습니다. 예를 들면 어플리케이션은 다음과 같은 패스가 생길 수 있습니다.

/publishers/1/magazines/2/photos/3

여기에 대응되는 라우트 헬퍼는 publisher_magazine_photo_url 이 됩니다. 3단계 레벨에 따른 특정한 객체도 필요로 됩니다. 실제로 이러한 상황은 충분히 혼란 스러운 상황입니다. 그러므로 중첩 리소스 선언은 1단계 레벨을 넘는 설정을 하지 않는것을 권장합니다. (영어로 never로 표현)

2.7.2 얕은 중첩

위에서 권장한 깊은 중첩을 피하기 위한 한가지 방법으로 배열을 선언하여 스코프를 특정짓는 방법이 있습니다. 다음과 같이 리소스를 식별하는데 필요한 최소한의 정보만으로 라우팅이 가능하도록 설정하는 방법입니다.

resources :posts do
  resources :comments, only: [:index, :new, :create]
end
resources :comments, only: [:show, :edit, :update, :destroy]

위의 설정을 잘 보면 알 수 있습니다만 특정 코멘트의 show, edit, update, destroy는 posts이하에 있을 필요가 없습니다. comments만의 고유 식별자로 처리가 가능한 부분입니다. 하지만 index, new, create의 경우 posts에 종속적인 데이터로 관리되어야 하는 부분이기에 posts에 종속됩니다.

이러한 방법은 설명이 가능한 라우트와 깊은 라우트간에 균형을 유지하는 느낌을 줍니다. 위의 정의를 손쉽게 할 수 있는 방법으로 :shallow 옵션을 사용하는 방법이 있습니다.

resources :posts do
  resources :comments, shallow: true
end

위의 단순한 설정은 정확하게 먼저 보여드린 복잡한 설정과 동일하게 동작합니다. :shallow 옵션을 부모 리소스에 사용하게 되면 모든 중첩 리소스들에 적용됩니다.

resources :posts, shallow: true do
  resources :comments
  resources :quotes
  resources :drafts
end

비슷하게 shallow 메소드를 사용하게 되면 해당 스코프의 모든 중첩 리소스들에 얕은 중첩이 적용됩니다. 이러한 설정은 위의 정의와 정확하게 동일한 결과를 보여줍니다.

shallow do
  resources :posts do
    resources :comments
    resources :quotes
    resources :drafts
  end
end

위에서 설명한 방법을 커스터마이징하여 특정 스코프를 적용한 얕은 라우트를 정의할 수 있습니다. 패스를 정의하는 파라미터와 함께 :shallow_path 를 정의하면 됩니다.

scope shallow_path: "sekret" do
  resources :posts do
    resources :comments, shallow: true
  end
end

comments 리소스는 다음과 같은 라우팅 정의를 갖게 됩니다.

 

[L4 SWITCH] DSR 모드에서의 서버 네트워크 설정 방법

이번글은 서버관리자 입장에서의 글입니다. 네트워크 분야에서 일하시는 분들에게는 별볼일 없는 내용일 것입니다.

하지만 서버 관리자입장에서는 모르시는 분들도 있을꺼 같아 글을 적어봅니다.

보통 수많은 서버를 가지고 서비스를 하는 업체라면 L4 스위치가 한대이상 존재할 것입니다.

L4 스위치한대만으로도 엄청나게 빠른 속도로 많은 량의 분산처리를 위한 라우팅을 완벽하게 해내죠.

L4란 OSI 7계층을 공부하시면서 들오보셨을 텐데 Layer 4 라우팅을 하는 스위치라는 뜻입니다.

L4 스위치의 라우팅 모드중에 DSR(Direct Server Return)이라는 평범하지 않은 모드가 있습니다.

말그대로 서버에서 결과를 바로 리턴한다는 뜻이지요. 어떤것인지 다음의 그림을 보면서 설명 드리겠습니다.

사용자 삽입 이미지
위와 같은 네트워크 구조가 있다고 합시다. 위의 구조는 전형적인 DSR을 위한 구조이지만 우선 평범한 SLB(Server Load Balancer) 에 대해 설명 드리겠습니다.

편의를 위해 L3 스위치의 아이피는 표시하지 않았습니다. 123.123.123.123은 외부에 공개되는 아이피입니다.

저 아이피를 통해 웹서비스에 접속할 수 있습니다. 10.1.1.x 대의 아이피들은 잘 아시겠지만 내부 네트워크입니다.

이것을 보시면 외부 인터넷에서 L4를 거치지 않고는 웹서버들에게 접근할 수 없다는 것을 알 수 있습니다.

일반적인 로드밸런싱은 다음과 같은 방법으로 이루어 집니다. DSR용 구조라 설명이 좀 복잡합니다;;

SLB 접속 :

Internet → A → L3 Switch → B  → L4 Switch → C → L3 Switch → D → Web Server 1 → D → L3 Switch → C → L4 Switch → B → L3 Switch → A → Internet

설명을 적으면서 계속 잘못되었다는 생각이 들지만 중요한점은 클라이언트의 Request와 서버의 Response가 둘다 L4를 거쳐간다는것만 알아주시면 됩니다.

하지만 DSR구조는 L4가 서버들의 로드밸런싱은 하지만 리턴은 서버에서 클라이언트에게 바로 리턴합니다. L4를 거치지 않습니다.

그렇다면 다음과 같은 방법으로 이루어 지겠죠.

DSR 접속 :

Internet → A → L3 Switch → B → L4 Switch → C → L3 Switch → D → Web Server 1 → D → L3 Switch → A → Internet

어떤 차이가 있는지 보이시나요? 답장은 L4를 거치지 않고 바로 클라이언트에게 전송됩니다.

하지만 여기서 서버에 추가적인 설정이 필요합니다.

클라이언트는 분명히 123.123.123.123으로 Request를 보냈기 때문에 마찬가지로 123.123.123.123으로부터 Response가 돌아와야 합니다.

하지만 10.1.1.2아이피를 가지고 있는 서버가 반환을 하였죠. 클라이언트의 컴퓨터는 답장을 받았지만 잘못된 패킷으로 인지하고 버려버리게 됩니다.

이래선 통신이 제대로 되질 못하죠. 해결 방법은 서버의 Loopback 주소를 변경하면 됩니다.

하지만 제가 무식하게 lo의 아이피를 127.0.0.1에서 123.123.123.123으로 바꿨더니 서버가 바보가 되더군요.

채널본딩을 사용하면 됩니다. lo가 특수한 장치라서 정상적인 설정 방법으로는 채널본딩이 안되지만 다음과 같은 명령어는 잘됩니다.

/sbin/ifconfig lo:0 123.123.123.123 netmask 255.255.255.255

이때 주의할것인 서브넷마스크가 255.255.255.255여야 한다는겁니다. 이렇게 함으로써 잘못된 Loopback주소를 브로드캐스팅 하는것을 막을 수있습니다.

[eye@theeye /] $ ifconfig
eth0      Link encap:Ethernet  HWaddr 00:0B:DB:95:0E:E1  
          inet addr:123.123.123.123  Bcast:123.123.123.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:30871472 errors:0 dropped:0 overruns:0 frame:0
          TX packets:42803475 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:414432160 (395.2 MiB)  TX bytes:1433412018 (1.3 GiB)
          Interrupt:185



lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:30192781 errors:0 dropped:0 overruns:0 frame:0
          TX packets:30192781 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:1340081608 (1.2 GiB)  TX bytes:1340081608 (1.2 GiB)



lo:0      Link encap:Local Loopback  
          inet addr:123.123.123.123  Mask:255.255.255.255
          UP LOOPBACK RUNNING  MTU:16436  Metric:1

추가적으로 /etc/sysctl.conf에 다음의 내용을 추가하여 주도록 합시다.

net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.eth0.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2

이후에 sysctl -p 명령을 통해 설정을 적용하시면 됩니다. 잘 되나요?^^