fiber-tutorial-1.0.zip
0.05MB

본 튜토리얼에서는 대부분의 사이트에 기본적으로 필요한 기능을 익히는데 목적이 있습니다. 예제에서는 관리자로 로그인/로그아웃하고, 관리자를 등록/수정/삭제 등을 할 수 있는 기본적인 기능을 포함하는 예제를 만들어봅니다. 그리고, 비밀번호의 단방향 암호화(SHA-256)를 해보는 기능도 포함됩니다. DB 연결시 GORM을 사용하지만, 제공되는 Model 메소드(ORM)를 사용하지 않고, 직접 쿼리(스토어드 프로시저)를 사용하여 처리할 것입니다.

 

또한, 튜토리얼의 소스는 https://github.com/gauryan/fiber-tutorial 에서 확인 가능합니다.

  1. 시작 (설치 및 첫페이지 만들어보기)
  2. MySQL 설치와 연결
  3. 관리자 목록 보여주기
  4. 관리자 추가하기
  5. 관리자 비밀번호 변경하기
  6. 관리자 수정하기
  7. 관리자 삭제하기
  8. 비밀번호 단방향암호화(SHA256) 하기
  9. 로그인/로그아웃 처리하기

 

 

이번에는 관리자의 비밀번호를 변경해볼 것입니다. 이를 위해서 저장프로시저를 2개 생성할 것이고, 관련 코드들을 작성해보겠습니다. 코드들이 이전과 비슷하지만, 조금씩 다들 부분들이 있으니 주의해서 봐주세요.

 

그리고, 이 튜토리얼의 소스는 https://github.com/gauryan/fiber-tutorial에서도 확인할 수 있습니다.

 

1. 저장 프로시저 (getAdmin) 생성

$ mysql -u xyz -pxyz123 xyz
mysql: [Warning] Using a password on the command line interface can be insecure.
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 21
Server version: 8.0.26-0ubuntu0.20.04.2 (Ubuntu)

Copyright (c) 2000, 2021, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>
DELIMITER $$
CREATE PROCEDURE getAdmin(i_sno INT)
BEGIN
  SELECT sno, userid, password, nick FROM admins WHERE sno = i_sno LIMIT 1;
END $$
DELIMITER ;
mysql> CALL getAdmin(2);
+------+---------+----------+-------+
| sno  | userid  | password | nick  |
+------+---------+----------+-------+
|    2 | testid2 | passwd2  | nick2 |
+------+---------+----------+-------+
1 ROW IN SET (0.00 sec)
 
Query OK, 0 ROWS affected (0.00 sec)
 
mysql> exit

 

2. 뷰 디렉토리에 ~/project/xyz/views/mgmt/admin/chg_passwd_form.html 파일을 생성한다.

<div class="modal-header">
  <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
  <h4 class="modal-title">관리자 비밀번호 변경</h4>
</div>
<div class="modal-body">
  <form name="chg_passwd_form" action="/mgmt/admin/chg_passwd" method="post">
    <div class="form-group">
      <label>아이디</label>
      <input type="text" name="userid" class="form-control" readonly required value="{{ .Admin.Userid }}"/>
      <input type="hidden" name="id" value="{{ .Admin.Sno }}" />
    </div>
    <div class="form-group">
      <label>비밀번호 <small>(필수)</small></label>
      <input type="password" name="passwd1" class="form-control" required />
    </div>
    <div class="form-group">
      <label>비밀번호 확인 <small>(필수)</small></label>
      <input type="password" name="passwd2" class="form-control" required />
    </div>
    <div class="form-group" style="text-align: right">
      <input class="btn btn-primary" type="submit" value="관리자 비밀번호 변경" />
    </div>
  </form>
</div>

 

3. 컨트롤러(~/project/xyz/controllers/mgmt/admin.go)에 다음을 추가한다.

...
 
// 관리자 비밀번호변경 폼
// /mgnt/admin/chg_passwd_form/:id
func ChgPasswdForm (c *fiber.Ctx) error {
    type Admin struct {
        Sno    int
        Userid string
        Passwd string
        Nick   string
    }
    var admin Admin
 
    id := c.Params("id")
 
    db := database.DBConn
    db.Raw("CALL getAdmin(?)", id).First(&admin)
    data := fiber.Map{"Admin": admin}
    return c.Render("mgmt/admin/chg_passwd_form", data)
}
 
...

 

4. 라우터(~/project/xyz/routes/web.go)에 다음을 추가한다.

    mgmtApp.Get("/admin/chg_passwd_form/:id", mgmt.ChgPasswdForm)

 

5. 이제, 비밀번호변경 버튼을 클릭하면 비밀번호변경을 위한 모달 다이얼로그박스가 나타날 것이다.

 

6. 실제로 비밀번호를 변경하는 작업을 해보자. 우선 저장 프로시저 (updateAdminPassword) 생성하자.

$ mysql -u xyz -pxyz123 xyz
mysql: [Warning] Using a password on the command line interface can be insecure.
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 21
Server version: 8.0.26-0ubuntu0.20.04.2 (Ubuntu)

Copyright (c) 2000, 2021, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>
DELIMITER $$
CREATE PROCEDURE updateAdminPassword
    (i_sno INT,
     i_password VARCHAR(255))
BEGIN
    UPDATE admins SET password = i_password WHERE sno = i_sno;
END $$
DELIMITER ;
mysql> CALL listAdmins();
+-----+---------+----------+-------+
| sno | userid  | password | nick  |
+-----+---------+----------+-------+
|   1 | testid1 | passwd1  | nick1 |
|   2 | testid2 | passwd2  | nick2 |
|   3 | testid3 | passwd3  | nick3 |
|   4 | testid4 | passwd4  | nick4 |
|   5 | testid5 | passwd5  | nick5 |
|   9 | testid6 | passwd6  | nick6 |
+-----+---------+----------+-------+
6 ROWS IN SET (0.00 sec)
 
Query OK, 0 ROWS affected (0.00 sec)
 
mysql> CALL updateAdminPassword(1, 'passwd101');
Query OK, 1 ROW affected (0.00 sec)
 
mysql> CALL listAdmins();
+-----+---------+-----------+-------+
| sno | userid  | password  | nick  |
+-----+---------+-----------+-------+
|   1 | testid1 | passwd101 | nick1 |
|   2 | testid2 | passwd2   | nick2 |
|   3 | testid3 | passwd3   | nick3 |
|   4 | testid4 | passwd4   | nick4 |
|   5 | testid5 | passwd5   | nick5 |
|   9 | testid6 | passwd6   | nick6 |
+-----+---------+-----------+-------+
6 ROWS IN SET (0.00 sec)
 
Query OK, 0 ROWS affected (0.00 sec)
 
mysql> exit

 

7. 그리고, 컨트롤러(~/project/xyz/controllers/mgmt/admin.go)에 다음을 추가한다.

...
 
// 관리자 비밀번호변경
// /mgmt/admin/chg_passwd
func ChgPasswd (c *fiber.Ctx) error {
    id      := c.FormValue("id")
    passwd1 := c.FormValue("passwd1")
    passwd2 := c.FormValue("passwd2")
 
    if passwd1 != passwd2 {
        return c.Redirect("/mgmt/admin")
    }
    db := database.DBConn
    db.Exec("CALL updateAdminPassword(?, ?)", id, passwd1)
 
    return c.Redirect("/mgmt/admin")
}
 
...

 

8. 라우터(~/project/xyz/routes/web.go)에는 다음을 추가한다.

    mgmtApp.Post("/admin/chg_passwd", mgmt.ChgPasswd)

 

9. 비밀번호를 변경해보고, DB의 내용이 잘 반영되었는지 확인해보자.

$ mysql -u xyz -pxyz123 xyz
mysql: [Warning] Using a password on the command line interface can be insecure.
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 241
Server version: 8.0.26-0ubuntu0.20.04.2 (Ubuntu)
 
Copyright (c) 2000, 2021, Oracle and/or its affiliates.
 
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
 
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
 
mysql> CALL listAdmins();
+-----+---------+-----------+-------+
| sno | userid  | password  | nick  |
+-----+---------+-----------+-------+
|   1 | testid1 | passwd101 | nick1 |
|   2 | testid2 | passwd2   | nick2 |
|   3 | testid3 | passwd3   | nick3 |
|   4 | testid4 | passwd4   | nick4 |
|   5 | testid5 | passwd5   | nick5 |
|   9 | testid6 | passwd106 | nick6 |
+-----+---------+-----------+-------+
6 rows in set (0.00 sec)
 
Query OK, 0 rows affected (0.00 sec)
 
mysql>

본 튜토리얼에서는 대부분의 사이트에 기본적으로 필요한 기능을 익히는데 목적이 있습니다. 예제에서는 관리자로 로그인/로그아웃하고, 관리자를 등록/수정/삭제 등을 할 수 있는 기본적인 기능을 포함하는 예제를 만들어봅니다. 그리고, 비밀번호의 단방향 암호화(SHA-256)를 해보는 기능도 포함됩니다. DB 연결시 GORM을 사용하지만, 제공되는 Model 메소드(ORM)를 사용하지 않고, 직접 쿼리(스토어드 프로시저)를 사용하여 처리할 것입니다.

 

전체 9개의 글로 작성될 것이니, 이후에도 잘 챙겨서 봐주시면 아마도 어쩌면 도움이 되실지도 모르겠습니다. *^^*

이 Tutorial의 소스는 깃헙(https://github.com/gauryan/fiber-tutorial)에도 올려놓았습니다.

 

 

OS(Ubuntu Linux 20.04)계정은 기본계정인 ubuntu 를 사용하는 것으로 가정한다. 사용할 프로젝트 디렉토리는 ~/project/xyz 로 될 것이다. Go 언어도 설치되어 있다고 가정하겠습니다. 참고로 저는 1.17.1 버전이 설치되어 있습니다.

 

0. 프로젝트 디렉토리 구조

xyz
├-- controllers
├-- database
├-- routes
└-- views

Fiber는 디렉토리 구조를 강제하지 않기 때문에 마음대로 만드셔도 됩니다. 위의 구조는 제 마음대로 그냥 정한 것입니다.

 

1. 서비스에 사용할 웹서버(nginx)를 설치하고 설정 해보자.

$ sudo apt-get install nginx
$ cd /etc/nginx/sites-available
$ sudo vi xyz
server {
    listen 80;
    server_name xyz.test.com; # 자신이 원하는 도메인주소 입력

    location / {
      proxy_pass http://localhost:3000;
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection 'upgrade';
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-Proto $scheme;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_cache_bypass $http_upgrade;
    }
}
$ cd ../sites-enabled
$ sudo ln -s /etc/nginx/sites-available/xyz xyz

 

2. nginx 재시작

$ sudo /etc/init.d/nginx restart

 

3. 작업 디렉토리를 만들고, 모듈 초기화하기

$ cd ~
$ mkdir -p project/xyz
$ cd project/xyz
$ go mod init github.com/gauryan/xyz
go: creating new go.mod: module github.com/gauryan/xyz
$

모듈이름은 자신의 원하는 것으로 하세요~! 단, 이후 import 할 때, 주의하세요.

 

4. 하위 폴더 만들고, 시작 스크립트 만들기

$ mkdir controllers; mkdir database; mkdir routes; mkdir views
$ vi run.sh
go run main.go
 
$ chmod 755 run.sh

 

5. 이제, 첫화면을 위한 controller를 만들어볼까요? ~/project/xyz/controllers 디렉토리에 main.go 를 생성합니다.

package controllers
 
import (
    "github.com/gofiber/fiber/v2"
)
 
func Index(c *fiber.Ctx) error {
    data := fiber.Map{ "Title": "Hello, World!", }
    return c.Render("index", data)
}

Index 함수는 http://xyz.test.com 으로 접속할 때, 연결되는 것입니다. 이후에 index.html 을 열어서 보여주게 됩니다.

 

6. 그러면, ~/project/xyz/views/index.html 을 만듭니다.

<!DOCTYPE html>
<html>
<body>
  <h1>{{.Title}}</h1>
</body>
</html>

.Title은 컨트롤러에서 넘겨받은 데이터 “Hello, World!” 입니다.

 

7. URL과 컨트롤러를 연결해주는 Router를 작성합니다. 작성할 파일은 ~/project/xyz/routes/web.go 입니다.

package routes
 
import (
    "github.com/gofiber/fiber/v2"
    "github.com/gofiber/template/html"
    "github.com/gauryan/xyz/controllers"
)
 
 
func Router() *fiber.App {
    // App 생성과 템플릿 설정
    app := fiber.New(fiber.Config{
        Views: html.New("./views", ".html"),
    })
 
    // Route 설정
    app.Get("/", controllers.Index)
 
    return app
}

 

8. 이제, 마지막으로 ~/project/xyz/main.go 를 작성해봅시다.

package main
 
import (
    "github.com/gauryan/xyz/routes"
)
 
 
func main() {
    app := routes.Router()
    app.Listen(":3000")
}

 

9. 모듈 의존성 업데이트

$ go mod tidy

 

10. 개발서버 실행

$ ./run.sh
 ┌---------------------------------------------------┐  
 │                   Fiber v2.19.0                   │ 
 │               http://127.0.0.1:3000               │ 
 │                                                   │ 
 │ Handlers ............. 2  Processes ........... 1 │ 
 │ Prefork ....... Disabled  PID ............. 35382 │ 
 └---------------------------------------------------┘

 

 

11. PC의 hosts 파일에 xyz.test.com 을 설정한 후에, 브라우저에서 http://xyz.test.com 을 입력하면 출력화면을 볼 수 있을 것이다. 그러면, 설치완료~!

 

 

모듈을 사용해서 Go 프로그램을 작성하는 방법을 간단하게 알아보겠습니다.

 

프로젝트 디렉토리 생성

$ mkdir hello
$ cd hello

 

프로젝트 모듈 초기화

$ go mod init hello
 
or
 
$ go mod init github.com/gauryan/hello
$ cat go.mod
module github.com/gauryan/hello
 
go 1.17

 

hello.go 파일 작성

$ vi hello.go
package main
 
import "fmt"
 
func main() {
  fmt.Println("Hello, world.")
}

 

프로그램 빌드/실행

$ go build
$ ./hello
Hello, world.

 

프로그램 수정 (의존성 추가)

$ vi hello.go
package main
 
import (
  "fmt"
  "rsc.io/quote"
)
 
func main() {
  // fmt.Println("Hello, world.")
  fmt.Println(quote.Hello())
}

 

프로그램 빌드 (의존성 문제로 실패 확인)

$ go build
hello.go:5:3: no required module provides package rsc.io/quote; to add it:
        go get rsc.io/quote
$

 

필요한 모듈 추가 설치

$ go mod tidy
go: finding module for package rsc.io/quote
go: downloading rsc.io/quote v1.5.2
go: found rsc.io/quote in rsc.io/quote v1.5.2
go: downloading rsc.io/sampler v1.3.0
go: downloading golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
$
$ cat go.mod
module github.com/gauryan/hello
 
go 1.17
 
require rsc.io/quote v1.5.2
 
require (
        golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c // indirect
        rsc.io/sampler v1.3.0 // indirect
)

 

프로그램 빌드/실행

$ go build
$ ./hello
안녕, 세상.
$

+ Recent posts