CentOS 7 에서 Docker로 Redmine 설치

CentOS 7 에서 Docker로 Redmine을 설치해본다.

도커가 없이도 ruby부터 시작해서 rails, rake 등등 모든걸 하나하나 설치해나갈 수도 있지만 직접 해본 결과 이렇게 하면 온갖 오류에 시달리게 된다. 특히나 CentOS 7의 기본 ruby 버전이 2.0.0으로 많이 낮기 때문에 이 문제를 해결하는 것부터가 난관이다.

아무튼 도커로 편하게 사용하는 것을 추천한다.


AWS Lightsail 서버에 설치

CentOS 7 선택 메모리 512MB로 실행한다.

인스턴스가 생성되면 고정IP를 부여하여 연결한다.

yum check-update 명령으로 패키지 업데이트 할 것이 있는지 확인한 다음, sudo yum update -y 명령으로 모든 패키지를 업데이트하고 재부팅을 한다.


MariaDB 설치

레드마인이 쓰는 DB는 MySQL이나 MariaDB나 아무 것이나 가능하다. 난 MariaDB로 설치.

CentOS 7에는 MariaDB가 이미 패키지내에 포함되어 있지만, 현재 기준으로 5.5.64 버전이 들어가 있다. 최신 버전으로 업데이트하기 위해 https://downloads.mariadb.org 으로 이동.

현재 기준으로 10.4.8 버전이 가장 최신의 stable 버전이다.

소스를 다운 받아서 컴파일 설치하기보다는 yum을 이용하는게 더 낫다.

https://downloads.mariadb.org/mariadb/repositories 로 이동하여 자기가 사용하는 배포판 버전을 선택하면 하단에 yum 설정이 출력된다. 그대로 복사하여 /etc/yum.repos.d/MariaDB.repo 경로로 파일을 만들고 내용에 붙여넣는다.

  1. sudo yum install MariaDB-server MariaDB-client 명령으로 MariaDB를 설치한다.
  2. sudo systemctl enable mariadb 명령으로 MariaDB를 서비스 시작으로 활성화한다.
  3. sudo systemctl start mariadb 명령으로 서비스 시작
  4. sudo systemctl status mariadb 입력하여 서비스 상태 확인한다.

명령으로 시스템 시작시 자동서비스 시작, MariaDB를 시작하고 제대로 설정되었는지 확인한다.

sudo mysql_secure_installation 을 입력하여 DB 기초설정을 시작한다.

중간에 Switch to unix_socket authentication [Y/n] 이라고 나오며 입력을 요구하는데, 이에 대해서는 http://www.nemonein.xyz/2019/07/2254/ 에 설명되어 있다. 시스템의 계정과 mysql 유저계정을 매칭시킬 것인지 여부인데 이걸 하게되면 보안에 좋을 것 같긴한데 기존의 사용방식에 헷갈릴 것 같아서 n 로 입력했다.

설정 과정에서 루트 암호를 변경해주고 테스트 테이블과 익명 접속을 차단해준다.

mysql -u root -p 로 mariadb에 접속한다.

  1. CREATE DATABASE redmine CHARACTER SET utf8 COLLATE utf8_general_ci; 명령으로 데이터베이스 생성.
  2. CREATE USER 'redmine'@'%' IDENTIFIED BY 'aa12341234'; 로 유저와 패스워드 생성
  3. GRANT ALL PRIVILEGES ON redmine.* TO 'redmine'@'%'; 로 데이터베이스에 권한 부여
  4. FLUSH PRIVILEGES; 로 입력한 내용 바로 적용

여기까지 진행하면 MariaDB 데이터베이스 설치가 끝난다.


Redmine 설치

리눅스에서 레드마인을 설치하는 방법은,

  1. 일일히 다 하나씩 설치한다.
  2. 그냥 도커로 한방에 설치해버린다.

두가지가 있다.

1번을  선택하면 관련 라이브러리부터 시작해서 모두 다 하나씩 설치해야하는데 CentOS에서 이게 생각보다 쉽지 않다.

왜냐면 레드마인을 돌려야할 Ruby가 2.0.0 버전이 기본인데 이 버전으로는 gem이며 여러가지 프로그램에서 오류가 계속 발생한다. epel이나 scl 등의 추가 라이브러리를 쓰면 2.6 이상을 설치할 수 있지만 그것도 사실 쉽지는 않다.

개인적으로는 도커로 한방에 설치하는 것을 추천.


Redmine 설치 : Docker로 레드마인 설치

https://hub.docker.com/_/redmine 에서 레드마인 공식 도커이미지를 받을 수 있다.

설정은 다음과 같은 파일을 만들어서 실행하면 된다.

docker run -d \
--name redmine \
-p 80:3000 \
-e DB_ADAPTER=mysql2 \
-e DB_HOST=localhost \
-e DB_PORT=3306 \
-e DB_USER=redmine \
-e DB_PASS=aa12341234 \
-e DB_NAME=redmine \
-v /home/centos/docker/redmine/datadir:/usr/src/redmine/files \
--restart="always" \
docker.io/redmine

모든 설정이 제대로 된줄 알았는데 접속이 되었다 안되었다 하였다. 이유를 찾아보니 시스템 메모리가 512MB 밖에 되지 않는데 이 메로리를 거의 다 사용하고 있었다.

다음과 같은 방법을 통해 스왑 메모리를 증가시켜 준다.

https://blog.dongbumkim.com/archives/4303

이 방법을 쓰고 나면 오류가 없어졌다.

여기까지 했다면 일단 레드마인은 실행된다. 설정했던 포트 80번으로 접속하면 레드마인 접속이 가능하다. 관리자 초기비밀번호는 admin / admin


플러그인과 테마 설치

설치 이후 레드마인에 플러그인을 추가한다거나 테마를 추가하려면 도커 컨테이너 안에 들어가서 처리를 해야한다.

docker exec -it redmine bash 명령으로 내부로 들어간 다음, apt-get update 를 한번 해주고 /usr/src/redmine 이 레드마인 루트 경로이다.

플러그인을 설치하겠다면 plugins 디렉토리에 가서 원하는 플러그인을 다운로드 받으면 된다.

테마는 public/themes 디렉토리에 원하는 테마를 다운로드한다.

테마는 다운로드하면 바로 적용해볼 수 있지만, 플러그인은 레드마인 컨테이너를 재시작해야만 적용된다. sudo docker ps 명령으로 컨테이너 ID를 확인한 후, sudo docker restart [컨테이너ID] 를 입력하여 컨테이너를 재시작해주면 플러그인이 추가된 것을 확인할 수 있다.


참고자료

  1. https://brunch.co.kr/@cheuora/45
  2. https://jistol.github.io/its/2018/01/25/redmine-mysql-in-docker/
  3. http://www.kwangsiklee.com/2017/07/centos%EC%97%90%EC%84%9C-ruby%EB%A5%BC-rpm%EC%9C%BC%EB%A1%9C-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0/

svn2git으로 svn 데이터를 git으로 마이그레이션

SVN 데이터를 GIT으로 가져가기 위한 방법. svn2git 이라는 프로젝트를 이용한다.


svn2git 설치

먼저 svn과 git이 설치되어 있어야 한다. 또한 git-svn 도 설치되어 있어야 한다.

svn 이 프로젝트는 루비로 되어있으므로 일단 ruby부터 설치. 우분투에서는 apt-get install ruby 입력.

설치 후 gem install svn2git 을 입력하여 svn2git을 설치한다.

주의할 점이 이 모든 것들이 리눅스에서 이루어져야한다. 이유는 아래에.


커미터(작성자) 목록 만들기

svn에서 코드 커밋을 했던 사람들을 git에 있는 커미터로 매칭해서 변경해줘야한다.

참고자료에 있는 내용처럼 .svn2git 폴더를 생성한다.

svn 레포지토리에서 커미터리스트를 뽑으려면 다음의 명령을 실행하면 된다.

svn log|grep "^r[0-9]"|awk -F\| '{print $2}'| sed -e 's/^[ \t]*//'|sort|uniq

문제는 이 명령줄에 쓰인 grep, awk 등은 리눅스에 있는 명령어들이며 윈도우에서는 없는 명령들이다. 이 명령에 해당하는 부분을 직접 구현하던가 아니면 커미터리스트를 일일치 찾아서 파일을 작성해야한다.

이 명령을 수행하면 커미터 리스트가 나오고 그것을 authors 파일로 저장한다. authors 파일에는 각 아이디에 해당하는 유저이름과 이메일까지 매칭해서 작성해야 한다.


SVN2GIT 명령 수행시 특정 확장자 제거

비주얼스튜디오로 만들어진 프로젝트를 처리하다보면 exe, map, pdb 파일등이 포함되어 있는데 다음과 같은 명령을 통해 이런 파일들을 거를 수 있다.

svn2git http://repository.com --exclude '([^\s]+(?=\.(exe|map|pdb))\.\2)'

실제로 해보니 제대로 처리되지 않았다. exclude 옵션이 작동하지 않는듯.


참고

C++/Python 환경에서 Coveralls + Travis-CI로 코드커버리지 테스트

코드 커버리지에 대한 설명들. https://blog.outsider.ne.kr/954

코드 커버리지를 테스트하기 위한 방법들을 정리해본다.


C++에서 코드커버리지 테스트

C++이나 gcc를 사용하고 있다면 g++ 컴파일 옵션에 --coverage 를 넣는다. 만약, cmake를 사용한다면, 다음과 같은 코드로 코드커버리지 테스트와 coveralls까지 연동 가능하다

https://github.com/dongbum/DServer/blob/master/.travis.yml

language: cpp

os:
- linux

compiler:
- gcc

dist: bionic

before_install:
- pip install --user cpp-coveralls
- sudo add-apt-repository ppa:mhier/libboost-latest -y
- sudo apt update
- sudo apt-get install libjemalloc-dev libjemalloc1 libjemalloc-dev libjsoncpp-dev libjsoncpp1 libmysqlcppconn-dev libmysql++-dev libboost1.70-dev

env:
  global:
  - MAKEFLAGS=-j 2

script:
- cmake CMakeLists.txt
- make
- ./GameServer

after_success:
- coveralls --exclude external_library --exclude make_gcc --exclude vs_solution --exclude CMakeFiles --gcov-options '\-lp'
© 2019 GitHub, Inc.

cmake로는 따로 옵션이 필요 없었다.


Python에서 코드커버리지 테스트

https://docs.coveralls.io/python 와 coveralls-python 두가지를 추천하고 있다. python-coveralls로 설정하던 중 다음과 같은 에러를 만났다.

다음과 같이 간단한 Traivs-CI 스크립트로 커버리지 테스트를 성공했다.

language: python
python:
  - 3.6
install:
  - pip install -r requirements.txt
  - pip install coverage
  - pip install coveralls
script:
  - python3 update.py
  - coverage run update.py
after_success:
  - coveralls

다만 coverage 명령은 run을 같이 해야되기 때문에 프로그램 실행되어야만 한다. 그래서 이 방법은 바로 실행되는 파이썬 프로그램에 한해서만 가능하다.


Python Flask에서 코드커버리지 테스트

Flask 프레임워크를 사용하면 웹서버를 바로 실행시키는 형태로 프로그램이 작동하는지라 위의 coverage run 명령어로 테스트를 할 수가 없다. 이 명령으로 테스트를 하면 웹서버가 실행되고 프로세스가 종료되지 않기 때문에 travis의 빌드도 영원히 계속된다. 물론 이 상태로 10분 정도가 지나면 travis가 빌드실패로 처리해버린다. 따라서 Flask를 쓸때는 travis-ci에서 실제 프로그램을 실행시키는 형태가 아니라 코드커버리지 테스트만 수행해야한다. 이렇게 작동하기 위해서는 nose라는 또다른 코드커버리지 툴이 필요했다. travis-ci 스크립트는 다음처럼 같이 작성했다.

language: python
services:
  - mysql
python:
  - 3.6
before_install:
  - mysql -e 'CREATE DATABASE IF NOT EXISTS test;'
install:
  - pip install -r requirements.txt
  - pip install coverage
  - pip install coveralls
  - pip install nose
  - pip install nose-exclude
script:
  - nosetests --with-coverage --cover-erase --cover-package=board
after_success:
  - coveralls

nosetests는 프로그램을 실제로 수행하지 않고도 코드커버리지만 테스트할 수 있기에 Flask로 작성된 프로그램들은 nose를 이용해야할 것 같다. 번외로, Flask를 사용하면서도 위에 있는 coverage run 명령을 이용할 수도 있는데, 웹서버를 실행시킨 상태로 특정 경로(예를 들면, /exit 같은...)을 호출하면 웹서버가 스스로 종료되도록 하는 방법이다. 하지만 이 방법은 그야말로 테스트를 위해 경로를 만들고 테스트만을 위한 종료코드까지 만들어야하는 관계로 nose를 쓰는 방법이 더 나은 것 같다.


참고자료

UnityBuild와 pragma once

회사에 들어와서 쓰던 것 중, 성준씨의 도움으로 유니티빌드라는 컴파일 기법을 알게 되었는데...

https://openshortcuts.com/archives/922

이 개념은 헤더파일을 다시 컴파일하는 것을 막아보자가 중심이다. 이 내용을 읽어보며 그럼 #pragma once#ifndef 가드가 있는데 왜 이걸로 컴파일 시간이 줄어든다는 것인가...? 에 대해 생각해보다가 다음과 같은 글을 보게 되었다. https://oojjrs.tistory.com/32 이 글이 내가 궁금했던 것을 정확히 설명해두었다.

한 번만 include 하는 것은 모두 오브젝트 단위에서의 이야기다. 오브젝트가 다르면 제 아무리 이전에 8만줄짜리 파일을 포함하여 컴파일을 이미 진행했다 하더라도 컴파일러는 다시 컴파일을 진행해버린다는 이야기다.

이것이 바로 유니티빌드를 사용할 때 컴파일 속도가 올라가는 이유였던 것.

Github와 연동 가능한 LGTM.com

Github에 돌아다니다보면 다음과 같은 뱃지를 볼 수 있는데,

저기 있는 코드 퀄리티 뱃지가 무엇인지 찾아보니 LGTM.com이라는 서비스였다.

원래 LGTM은 Looks Good To Me의 약자로 깃허브에서 다른 사람의 코드를 보고 '아주 좋은 코드다.'라는 의미의 리플을 남길 때 쓰는 단어이다.

여튼 이 서비스는 깃허브 레포지토리의 소스코드를 읽어들여서 소스코드의 품질을 체크하고 보안이슈나 위험한 부분을 체크해주는 서비스이다.

https://lgtm.com 에 가서 가입하고 깃허브 레포지토리를 연동해야한다. 연동 후 소스코드의 품질을 자동으로 측정하고 위험부분을 표시해준다.

cppcheck 등의 프로그램과 거의 동일하다. 다만 깃허브와 연동하기 편하게 되어있고, 온라인으로 알아서 작동한다는 것이 편리하다.

내 레포지토리에서는

사용되지 않는 변수 등을 이렇게 지적해준다.

대략 C++, Python 등 메인 프로그래밍 언어들은 다 지원하므로 깃허브에 소스코드를 올려서 사용한다면 이러한 서비스를 같이 사용해보면 좋을 것 같다.

Flask + IIS 로 윈도우에서 서비스하기

Flask로 만든 라이센스서버를 윈도우에 올려보기로 한다. 테스트할 환경은 다음과 같다.

  • OS : Microsoft Windows 10
  • Python : 3.6.8
  • Flask로 작성된 어플리케이션

Flask와 IIS

원래는 Flask로 만들었으니 IIS에 붙이려고 했다. 그런데 그게 쉽지 않았다. IIS에 온갖 설정과 에러와 싸우며 조금씩 전진했지만 나는 지쳐갔고 정말 이게 최선인가...? 하는 생각이 들기 시작했다. 어차피 플라스크만으로도 괜찮은 웹서버 아니었던가? 굳이 IIS와 연동이 왜 필요한가? 에서 답을 찾지 못했다. 아파치나 nginx와 연동도 있었지만 이건 결국 플라스크로 만든 어플리케이션을 띄워놓은 상태에서 웹으로 들어오는 요청만 프록시 처리해주는 방식이라 더 의미 없는 것 같았다. 그래서 이걸 아예 윈도우서비스로 올릴 수 있으면 어떨까 싶었다.


Web.config 파일 생성

https://docs.microsoft.com/ko-kr/visualstudio/python/configure-web-apps-for-iis-windows?view=vs-2019

웹페이지를 보며 설정한다. 간단하게 web.config 파일만 생성해주면 되었다. ```

<?xml version=1.0 encoding=utf-8?>
<configuration>
  <system.webServer>
    <handlers>
      <add   name=PythonHandler
            path=*
            verb=*
            modules=FastCgiModule
            scriptProcessor=C:\Python\Python36\python.exe|C:\Python\Python36\Lib\site-packages\wfastcgi.py
            resourceType=Unspecified
            requireAccess=Script ></add>
    </handlers>

  </system.webServer>

  <appSettings>
      <add key=PYTHONPATH value=D:\license_server></add>
      <!-- The handler here is specific to Bottle; see the next section. -->
      <add key=WSGI_HANDLER value=runserver.application></add>
      <add key=WSGI_LOG value=D:\logs\wfastcgi.log></add>
  </appSettings>
</configuration>

wfastcgi-enable 실행

cmd를 관리자 권한으로 열고, 파이썬 프로그램이 있는 곳에 가서 wfastcgi-enable 을 실행해준다. IIS에서 사이트를 생성하고 접속해본다. 파일을 생성하고 실행하니 이상하게도 config 파일을 읽어오지 못하는 문제가 발생했다. 경로도 모두 맞는데 파일을 읽지 못하는 현상. 확인해봤더니 root 경로를 잘못 잡고 있었다. 경로 밑에 config 파일을 넣어주니 해결


참고자료

apache 2.4 + flask 설정방법

아파치에서 Flask로 만들어진 어플리케이션을 작동하기 위한 방법을 정리해둔다.

 

에러메시지는

[Mon Jul 03 19:42:27.334280 2017] [:error] [pid 14570] [client 59.10.233.103:63211] Traceback (most recent call last):
[Mon Jul 03 19:42:27.334301 2017] [:error] [pid 14570] [client 59.10.233.103:63211] File "/home/viper9/python_test/VeloWeight.wsgi", line 7, in <module>
[Mon Jul 03 19:42:27.334349 2017] [:error] [pid 14570] [client 59.10.233.103:63211] from hello import app as application
[Mon Jul 03 19:42:27.334364 2017] [:error] [pid 14570] [client 59.10.233.103:63211] File "/home/viper9/python_test/hello.py", line 1, in <module>
[Mon Jul 03 19:42:27.334392 2017] [:error] [pid 14570] [client 59.10.233.103:63211] from flask import Flask
[Mon Jul 03 19:42:27.334409 2017] [:error] [pid 14570] [client 59.10.233.103:63211] ImportError: No module named flask
[Mon Jul 03 19:42:27.432767 2017] [:error] [pid 14538] [client 59.10.233.103:63210] mod_wsgi (pid=14538): Target WSGI script '/home/viper9/python_test/VeloWeight.wsgi' cannot be loaded as Python module., referer: http://python.83rpm.com/
[Mon Jul 03 19:42:27.432821 2017] [:error] [pid 14538] [client 59.10.233.103:63210] mod_wsgi (pid=14538): Exception occurred processing WSGI script '/home/viper9/python_test/VeloWeight.wsgi'., referer: http://python.83rpm.com/
[Mon Jul 03 19:42:27.432848 2017] [:error] [pid 14538] [client 59.10.233.103:63210] Traceback (most recent call last):, referer: http://python.83rpm.com/
[Mon Jul 03 19:42:27.432870 2017] [:error] [pid 14538] [client 59.10.233.103:63210] File "/home/viper9/python_test/VeloWeight.wsgi", line 7, in <module>, referer: http://python.83rpm.com/
[Mon Jul 03 19:42:27.432904 2017] [:error] [pid 14538] [client 59.10.233.103:63210] from hello import app as application, referer: http://python.83rpm.com/
[Mon Jul 03 19:42:27.432914 2017] [:error] [pid 14538] [client 59.10.233.103:63210] File "/home/viper9/python_test/hello.py", line 1, in <module>, referer: http://python.83rpm.com/
[Mon Jul 03 19:42:27.432928 2017] [:error] [pid 14538] [client 59.10.233.103:63210] from flask import Flask, referer: http://python.83rpm.com/
[Mon Jul 03 19:42:27.432963 2017] [:error] [pid 14538] [client 59.10.233.103:63210] ImportError: No module named flask, referer: http://python.83rpm.com/

http://flask.pocoo.org/docs/0.12/deploying/mod_wsgi/ 를 읽어보니 다음과 같이 써있었다.

 

Working with Virtual Environments
Virtual environments have the advantage that they never install the required dependencies system wide so you have a better control over what is used where. If you want to use a virtual environment with mod_wsgi you have to modify your .wsgi file slightly.

Add the following lines to the top of your .wsgi file:

activate_this = '/path/to/env/bin/activate_this.py'
execfile(activate_this, dict(file=activate_this))
For Python 3 add the following lines to the top of your .wsgi file:

activate_this = '/path/to/env/bin/activate_this.py'
with open(activate_this) as file_:
exec(file_.read(), dict(file=activate_this))

 

execfile(activate_this, dict(file=activate_this)) 를 삭제하고

 

with open(activate_this) as file_:
exec(file_.read(), dict(file=activate_this))

 

를 추가했다.

 

하지만 다시 실행해도 같은 에러...

 

wsgi 파일을 다음과 같이 수정했다.

import os
import sys
import site

site.addsitedir('/home/viper9/python_test/venv/lib64/python3.4/site-packages')

sys.path.insert(0, '/home/viper9/python_test')

activate_this = '/home/viper9/python_test/venv/bin/activate_this.py'
with open(activate_this) as file_:
exec(file_.read(), dict(file=activate_this))

from hello import app as application

 

MySQL 에러가 난다면 다음의 패키지를 설치한다.

yum install MySQL-python

 

해결했더니 그 다음 에러...

UnicodeDecodeError: 'ascii' codec can't decode byte 0xae in position 8: ordinal not in range(128)

 

https://libsora.so/posts/python-hangul/ 를 찾아보니 인코딩의 문제라고 한다. 문서를 보고 해결. .wsgi 에 설정을 추가했다.

reload(sys)
sys.setdefaultencoding('utf-8')

그런데... 그래도 해결이 안된다... 아 힘들다. 다시 이런저런 정보를 찾아헤메이다 로그를 다시 읽어보니 DB를 사용하는 곳에서만 에러가 났다. DB에서 데이터 가져올 때 문제가 있구나.. 라는 생각이 들어 DB 설정을 확인했더니 utf-8로 이미 다 설정되어 있었다. 코드도 UTF-8이고 DB도 UTF-8인데 데이터 가져오는데 문제가 있다면 커넥션 자체에서 캐릭터셋을 지정해주지 않아서 그런 것 같았다. 다시 검색을 해보니 다음 문서를 발견 https://stackoverflow.com/questions/10819192/sqlalchemy-result-for-utf-8-column-is-of-type-str-why create_engine() 함수를 쓸 때

create_engine('mysql+mysqldb:///mydb?charset=utf8')

처럼 사용하라는 것이었다. 이 설정을 추가해주니 드디어 페이지가 정확하게 떴다. 아이고 힘들다...

Visual Studio 에서 cURL 설치

윈도우에서 웹페이지와 통신할 때 WinHTTP 모듈을 쓰고 있었는데 이 모듈은 윈도우 전용이므로 리눅스에서는 사용이 불가능하다.

cURL 모듈을 사용할 수 있는지 테스트.

vcpkg로 cURL을 간단히 설치할 수 있었다.

그러나....

도저히 해결이 불가능했다. 구글과 스택오버플로우를 열심히 뒤졌으나 빌드플랫폼이 맞지 않아서 그럴거라는 의견뿐. x86 라이브러리와 x64 라이브러리를 모두 받아서 테스트 해봤으나 실패했다.

vcpkg에 대한 환상이 좀 깨졌다.

 

직접 받아서 빌드해보기로 했다.

cURL은 git 에서 소스 관리를 하고 있다.

cURL을 클론한다.

git clone https://github.com/curl/curl.git curl.git

다운 받은 폴더의 projects/Windows 폴더에 가면 Visual Studio 버전별로 쭉 있다. 난 2015이므로 VC12 선택.

curl-all.sln 솔루션 파일을 더블클릭하여 VS2015로 열어본다.

왜인지 모르겠는데 프로젝트 로드가 안된다. 프로젝트 파일이 없나 해당 위치에 가서 살펴봤는데.... 진짜로 프로젝트 파일이 없다!

 

어떻게 해야되나 고민하던 중에 project 폴더를 보니 여러개의 배치파일이 보였다. 일단 checksrc.bat 파일을 실행했다. 한참 아무메시지가 없더니만 프로그램이 종료되었다. 별 문제가 없으면 별 메시지를 출력하지 않나보다. (bat 파일 내용을 읽어보진 않았다.)

이제 왠지 프로젝트를 생성해줄 것 같은 generate.bat 파일을 실행했다. 예상대로...

D:\Library\curl.git\projects>checksrc.bat

D:\Library\curl.git\projects>generate.bat

Generating prerequisite files
* D:\Library\curl.git\Makefile
* D:\Library\curl.git\src\tool_hugehelp.c

Generating VC6 project files
* D:\Library\curl.git\projects\Windows\VC6\src\curl.dsp
* D:\Library\curl.git\projects\Windows\VC6\lib\libcurl.dsp

(...중략...)

Generating VC12 project files
* D:\Library\curl.git\projects\Windows\VC12\src\curl.vcxproj
* D:\Library\curl.git\projects\Windows\VC12\lib\libcurl.vcxproj

(...중략...)

D:\Library\curl.git\projects>

예상대로 프로젝트 파일을 생성해주었다.

이제 다시 아까 VC12 폴더로 가서 솔루션 파일을 여니까 프로젝트가 보인다! 솔루션 빌드를 눌렀더니....

 

...안된다...

 

1>d:\library\curl.git\lib\ssh.h(28): fatal error C1083: 포함 파일을 열 수 없습니다. 'libssh2.h': No such file or directory
1>..\..\..\..\lib\md5.c(88): fatal error C1083: 포함 파일을 열 수 없습니다. 'openssl/md5.h': No such file or directory
1>..\..\..\..\lib\md4.c(31): fatal error C1083: 포함 파일을 열 수 없습니다. 'openssl/opensslconf.h': No such file or directory

메시지가 쭉 박혀있었다.

다시 project 폴더에 가서 왠지 ssl 문제를 해결해줄 것 같은 build-openssl.bat 파일을 실행시키니 다음과 같은 안내가 나왔다.

D:\Library\curl.git\projects>build-openssl.bat

Usage: build-openssl <compiler> [platform] [configuration] [directory] [-VSpath] ["VSpath"] [-perlpath] ["perlpath"]

Compiler:

vc6       - Use Visual Studio 6
vc7       - Use Visual Studio .NET
vc7.1     - Use Visual Studio .NET 2003
vc8       - Use Visual Studio 2005
vc9       - Use Visual Studio 2008
vc10      - Use Visual Studio 2010
vc11      - Use Visual Studio 2012
vc12      - Use Visual Studio 2013
vc14      - Use Visual Studio 2015
vc14.1    - Use Visual Studio 2017

Platform:

x86       - Perform a 32-bit build
x64       - Perform a 64-bit build

Configuration:

debug     - Perform a debug build
release   - Perform a release build

Other:

directory - Specifies the OpenSSL source directory

-VSpath - Specify the custom VS path if Visual Studio is installed at other location
          then "C:/<ProgramFiles>/Microsoft Visual Studio[version]
          For e.g. -VSpath "C:\apps\MVS14"

-perlpath - Specify the custom perl root path if perl is not located at "C:\Perl" and it is a
            portable copy of perl and not installed on the win system
            For e.g. -perlpath "D:\strawberry-perl-5.24.3.1-64bit-portable"

D:\Library\curl.git\projects>

어렵구나...

OpenSSL을 같이 연동해줘야하는것 같은데 정녕 이 방법 밖에 없는 것인지 다시 생각해봤다.

 

다시 vcpkg를 쓰는 방법을 찾아본다.

 

많은 글들을 참고로 했다. ( http://bugsfixed.blogspot.com/2017/05/vcpkg.html )

이것저것 해보던 중. 갑자기 프로그램이 정상작동 되었다. -_-;;

설정은 프로젝트 설정에서 추가 포함 디렉토리로 include와 추가 라이브러리 디렉토리로 lib 디렉토리만 설정했고 다른 설정은 모두 삭제했다.

프로그램을 빌드해봤더니 바이너리 파일과 함께 libcurl.dll, zlib1.dll 파일이 자동으로 생성되었다. 이제 cURL을 테스트 해볼 차례.

 

<참고자료>

  • cURL 사용하여 웹페이지 긁어오기 : http://sobusted.cf/221485638633
    이 예제 중 fstream 관련하여 약간 문제가 있어 수정해서 사용했다.
  • libcurl 메뉴얼 : https://curl.haxx.se/libcurl/c/
  • 테스트 해봤던 프로젝트 : https://github.com/dongbum/CURLTest

double 타입 사용시 주의할 점

오늘 삽질했던 코드. 평소에는 double을 잘 안 쓰다보니 헷갈렸다.

문제가 되었던 코드.

for (auto iter = range.first; iter != range.second; iter++)
{
    int reward_id = (*iter).second.reward_id;
    int funcsrl = (*iter).second.funcsrl;
    int reward_type = (*iter).second.reward_type;
    std::string reward_itemcode = (*iter).second.itemcode;
    double rate = (*iter).second.rate / 100;	// DB에는 만분률로 되어있으므로 백분율로 환산해야함.

    lottery_info.Add(std::make_tuple(reward_id, funcsrl, reward_type, reward_itemcode), rate);

#ifdef _DEBUG
    ServerStateLog(g_DebugTracerServer, "RewardID:[%d] FuncSRL:[%d] Rate:[%f]", reward_id, funcsrl, rate);
#endif
}

확률 계산처리를 해야하는데 DB에는 데이터가 만분율 기준(ex : 9.5% = 950)으로 들어가 있고 확률 계산처리 함수에는 백분율로 넣어줘야했다. 그래서 데이터를 가져오면서 나누기 100을 했던 것인데...

이터레이터 안의 (*iter).second.rate 값은 int 값. 950을 100으로 나누어 그 결과인 9.5를 double 형으로 얻으려는 것이었다.

확률 계산에서 문제가 계속 일어나 찾아보니 저 값이 9.5가 아닌 9.0이 들어가고 있었다.

int 와 int 를 나누어서 double 에 넣는다고 소수점 처리가 되지 않는다는 것을 깜빡했다. / 100 을 / 100.0f 로 수정하는 것으로 쉽게 해결.

 

가변인자를 string 으로 사용하기

C/C++에는 함수의 인자를 가변으로 다룰 수 있도록 되어있는데 이 때 사용하는 것이 va_list, va_start, va_arg, va_end 이렇게 네가지 함수다. 네가지 함수에 대한 설명은 이미 인터넷에 많이 나와있다. 그런데 이 가변인자를 문자열 형태로 사용하려고 하면 문제가 많아서 이를 테스트하는 코드를 만들었다.

void Test(const char* types, ...)
{
    va_list ap;

    va_start(ap, types);

    while (types)
    {
        // std::cout &lt;&lt; code :  &lt;&lt; types &lt;&lt; std::endl;
        types = va_arg(ap, const char*);
    }

    va_end(ap);
}

#define Test(...) Test(__VA_ARGS__, NULL)

int main()
{
    Test(A123, A234, A345);

    return 0;
}

가변인자로 넣을 때는 std::string 으로 하려고 했으나 이 타입이 먹히지 않아 const char* 로 받아야한다. 중간에 define 으로 함수와 같은 이름의 매크로를 선언하고 가변인자의 맨 뒤에 NULL 을 넣어주도록 했는데 이게 없다면 가변인자의 맨 끝을 알 수 없어서 문제가 생기게 된다.


참고