Task manager app with Ruby on Rails(할일 관리 도구 만들기)

할일 관리 도구? 가 필요해서 뭐 쓸까 고민하다 걍 rails로 만들고 있는데, 별건 아니지만 그냥 글로 남겨둡니다.

https://gph.is/XGFOUK

Rails Setup

최근에 Vim => Rubymine 으로 개발 환경 변경을 했던지라 대다수 과정이 자동화되서 엄청 편했음.. 프로젝트 하나 만들어주시고.. 혹시라도 콘솔 작업이시면 ..

rails new pjpj

이고 저희 경우는 postgresql을 쓸거라 db를 저걸로 맞춰줬습니다. 개인의 취향따라 고고고 합시다.

DB(PostgreSQL on Rails) 세팅하기

우선 저는 postgresql을 쓰기 때문에, 해당 앱을 위한 계정을 생성해줘야합니다. (사실 뭐 통합으로 써도 되긴하지만, 이왕이면 권한분리)

계정생성

createuser hahwul

기본적으로 패스워드 설정이 안되어 있는데 다른 DB와 동일하게 쿼리로 세팅해줄 수 있습니다. psql로 query 진입 후 alter user로 바꿔줍시다.

패스워드 설정

psql
psql=# alter user hahwul with encrypted password ‘your_password'

그러고 다시 쉘로 나와서 db도 생성해줍니다.

DB 생성

createdb hwul_db

이제 아까 만든 계정에 이 db 권한을 줍시다.

권한 부여

psql
psql=# grant all privileges on database hwul_db to hahwul

일단 여기까지 하면 postgresql에서의 세팅은 끝입니다. 이제 rails 앱쪽으로 가서 db 정보를 수정해줍시다.

vim database.yml
production:
  <

개발환경에서 테스트하거나, 유닛 테스트하려면 develperment랑 test도 지정해 주셔야합니다. (개인의 취향따라..)

이게 예전에는 database.yml에 직접 패스워드를 기입하는 형태로 많이했었는데, 솔직히 썩 좋은 방법은 아닙니다. 환경변수로 지정해주고 로그하는게 훨씬 좋아요 :) (혹시라도 git에 올리면 안남으니깐)

export HAHWUL_DATABASE_PASSWORD=your_password

후우 이제 rails의 db가 postgresql로 연결됩니다.

전체적인 구조 만들기

할일을 관리(?)하는 도구이기 때문에 요정도 있음 어떨까 싶습니다.

title : 할일 제목 body : 내용 state : 상태(false=미진행, true=완료) add_date : 등록 날짜 modified_date : 최근 수정일(나중에 리스트 너무 커지면 날짜 기준으로 정렬하며 어떨까해서..예를들면 한주 ㅋㅋ) tag : 이건 실제로 추가는 안했느데, 글쓰다보니 생각나서 포함했습니다. 나중에… db 수정해서 추가해야겠네요.

rails으 강점은 역시 scaffold!

rails g scaffold Work title:string body:text state:boolean add_date:datetime modified_date:datetime

db migrate 해주면,

rake db:migrate

일단 기본적인 CRUD랑 Data model, 아주 기본적인 View는 완료되었네요. 공통 css랑 index 코드 몇개 수정했습니다.

.wrapper{
    width: 100%;
}
.wrapper.td{
    width:33.3%
}
<p id="notice"><%= notice %></p>

<%
  job_success = Array.new
  job_list = Array.new

  @works.each do |work|
    if work.state
      job_success.push(work)
    else
      job_list.push(work)
    end
  end
%>

<%= link_to 'New Work', new_work_path %>
<hr>

<table class="wrapper">
  <tr>
    <td>Detail</td>
    <td>Works</td>
    <td>Success</td>
  </tr>
  <tr>
    <td style="border-right: 1px solid #333333"></td>
    <td>
      <table>
      <% job_list.each do |work| %>
          <tr>
            <td><%= work.title %></td>
            <td><%= work.add_date %></td>
            <td><%= work.modified_date %></td>
            <td><%= link_to 'Show', work %></td>
            <td><%= link_to 'Edit', edit_work_path(work) %></td>
            <td><%= link_to 'Destroy', work, method: :delete, data: { confirm: 'Are you sure?' } %></td>
          </tr>
      <% end %>
      </table>
    </td>
    <td>
      <table>
        <% job_success.each do |work| %>
          <tr>
            <td><%= work.title %></td>
            <td><%= work.add_date %></td>
            <td><%= work.modified_date %></td>
            <td><%= link_to 'Show', work %></td>
            <td><%= link_to 'Edit', edit_work_path(work) %></td>
            <td><%= link_to 'Destroy', work, method: :delete, data: { confirm: 'Are you sure?' } %></td>
          </tr>
        <% end %>
      </table>
    </td>

  </tr>
</table>

자 빠르게 기능 몇개만 더 추가해보죠.

상태 변경 버튼 만들기

버튼 클릭하면 완료 / 재오픈 할 수 있는 기능이 제일 우선이였습니다. (언제 수정 누르고 이썽..) 일단 버튼을 누르면 자동으로 state 값을 변화시킬거고, 이는 로직만 있는 부분이니 controller 하나 만들어주고 기능을 엮어봅시다.

rails g controller updatework to_success to_list

네이밍 엄청 거지 같긴한데, updatework 라고 지었어요… updatework controller에 to_success(완료처리), to_list(재오픈) 을 사용할거라고 같이 인자로 넣어줍니다. 컨트롤러를 만들면 기본적으로 라우팅이 GET으로 잡혀있습니다. 기능 처리 부분이고 HTML 페이지를 렌더링해줄 것도 아니니, POSt로 바꿔줍시다(이게 더 어울린다고 생각했어요)

route.rb 에서 post로 수정

Rails.application.routes.draw do
  post 'updatework/to_success'
  post 'updatework/to_list'
  resources :works
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end

그럼 이제 updatework/to_sucess , updatework/to_list로 POST 요청이 발생하면 해당 controller의 로직을 타게 됩니다.

우선 index에서 button 포맷을 하나 추가해주고.. updatework를 바라보게 코드 작성합니다. work가 위에 work contoller의 처리 결과(각 개별 데이터)인데, 이를 인자값으로 넘겨줍시다(실제 요청에선 넘버링된 값)

index.html.erb

<%= button_to "Success", updatework_to_list_path, params: {id: work} %>

그리고.. updatework의 controller 내용을 채워줍시다.

updatework_controller.rb

class UpdateworkController < ApplicationController
  before_action :set_list, only: [:to_list, :to_success]
  # action이 들어오면 set_list 함수를 호출해서
  # Work 객체에서 id값으로 해당 데이터를 찾아서 @work에 저장합니다
 
  def to_success
    # 완료 처리일 때 state 를 true로
    @work.update(:state => true)
    redirect_back fallback_location: 'works'
  end

  def to_list
    # 재오픈일 때 false로 변경해줍시다.
    @work.update(:state => false)
    redirect_back fallback_location: 'works'
  end

  # 모두 redirect는 이전 페이지로 돌려줍시다.

  def set_list
    @work = Work.find(params[:id])
  end
end

혹시 모르니 index 전체 코드를 같이 첨부합니닷.

index.html.erb 전체 코드

<p id="notice"><%= notice %></p>

<%
  job_success = Array.new
  job_list = Array.new

  @works.each do |work|
    if work.state
      job_success.push(work)
    else
      job_list.push(work)
    end
  end
%>

<%= link_to 'New Work', new_work_path %>
<hr>

<table class="wrapper">
  <tr>
    <td>Detail</td>
    <td>Works</td>
    <td>Success</td>
  </tr>
  <tr>
    <td style="border-right: 1px solid #333333"></td>
    <td>
      <table>
      <% job_list.each do |work| %>
          <tr>
            <td><%= work.title %></td>
            <td><%= work.add_date %></td>
            <td><%= work.modified_date %></td>
            <td><%= link_to 'Show', work %></td>
            <td><%= link_to 'Edit', edit_work_path(work) %></td>
            <td><%= link_to 'Destroy', work, method: :delete, data: { confirm: 'Are you sure?' } %></td>
            <td><%= button_to "Success", updatework_to_success_path, params: {id: work} %></td>
          </tr>
      <% end %>
      </table>
    </td>
    <td>
      <table>
        <% job_success.each do |work| %>
          <tr>
            <td><%= work.title %></td>
            <td><%= work.add_date %></td>
            <td><%= work.modified_date %></td>
            <td><%= link_to 'Show', work %></td>
            <td><%= link_to 'Edit', edit_work_path(work) %></td>
            <td><%= link_to 'Destroy', work, method: :delete, data: { confirm: 'Are you sure?' } %></td>
            <td><%= button_to "Reopen", updatework_to_list_path, params: {id: work} %></td>
          </tr>
        <% end %>
      </table>
    </td>

  </tr>
</table>

자 이제 Success, Reopen 버튼 누를 때 updatework > to_list, to_success 함수 호출로 Work 내 state 값을 업데이트해서 변경이 가능합니다.

API로 추가 연동하기는 개뿔 걍 나중에 할꺼

TODO 인데, 뭔가 작업 리스트를 받아올 수 있는 곳에 API를 사용할 수 있으면 연동해서 Works로 밀어넣어주면, 일부는 자동으로 입입되고, 완료될 것 같습니다. 대충 틀은 나왔으니 이 부분만 추가해서 회사에서 써야겠네요(사실 널린거 받아써도 되는데…)