Tag Archives: mysql

macOS Sierra 기준 Homebrew를 이용하여 APM 설치하기

macos-sierra-logo

이번에는 새로 출시된 macOS 시에라에서 Apache, PHP, MySQL을 설치하여 서비스로 구동하는 과정을 정리해보겠습니다. 이 모든작업들 역시 Homebrew를 사용하면 손쉽게 해낼 수 있습니다. 우선 이 글은 Homebrew가 정상적으로 설치되어있는 맥이 준비되어있다고 가정하고 작성되었습니다.

$ brew tap homebrew/dupes
$ brew tap homebrew/php
$ brew tap homebrew/apache
$ brew update

우선 설치할 것들에 대한 준비를 위해 tap 명령을 사용하여 별도의 저장소를 추가하도록 합니다. 이 명령은 Homebrew의 master 저장소에 없는 패키지들을 사용할 수 있도록 해줍니다.

MariaDB 설치

MySQL 대신에 제가 선호하는 MariaDB를 설치하도록 하겠습니다. 다음의 명령을 사용하여 매우 간단하게 설치할 수 있습니다.

$ brew install mariadb

설치가 완료되면 다음의 명령을 수행하여 초기에 필요한 기본 디비 구성을 할 수 있습니다.

$ mysql_install_db

이제 준비가 끝났으니 MariaDB를 서비스 형태로 구동해 보겠습니다.

$ brew services start mariadb

여기까지만 진행하면 익명 상태로 mysql 명령을 사용하여 자유롭게 사용이 가능한 상태가 됩니다만 보안에 취약한 상태가 됩니다. 물론 개인 맥북에서 사용하고 계신 상태라면 크게 문제될 것은 없습니다만 mysql_secure_installation 명령을 사용하면 손쉽게 root 비밀번호를 포함한 보안 상태의 설정을 변경할 수 있습니다.

$ mysql_secure_installation
Enter current password for root (enter for none): 
OK, successfully used password, moving on...

Set root password? [Y/n] Y
New password: 
Re-enter new password: 
Password updated successfully!
Reloading privilege tables..
 ... Success!

Remove anonymous users? [Y/n] Y
 ... Success!

Disallow root login remotely? [Y/n] Y
 ... Success!

Remove test database and access to it? [Y/n] Y
 - Dropping test database...
 ... Success!
 - Removing privileges on test database...
 ... Success!

Reload privilege tables now? [Y/n] Y
 ... Success!

Cleaning up...

All done!  If you've completed all of the above steps, your MariaDB
installation should now be secure.

Thanks for using MariaDB!

이제 mysql 명령을 사용하여 내가 결정한 비밀번호로 정상적으로 root 계정에 접속이 되는지 확인을 해봅시다.

$ mysql -uroot -p
Enter password: 
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 19
Server version: 10.1.18-MariaDB Homebrew

Copyright (c) 2000, 2016, Oracle, MariaDB Corporation Ab and others.

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

MariaDB [(none)]>

Apache 설치하기

macOS 시에라에는 이미 아파치 2.4 버전이 기본 설치되어있습니다. 하지만 지금 설치하려는 Homebrew 패키지들에 의해서 관리하기에 복잡함이 생기므로 기본 설치된 아파치 2.4는 꺼두고 새로운 아파치를 설치해보도록 하겠습니다.

$ sudo apachectl stop
$ sudo launchctl unload -w /System/Library/LaunchDaemons/org.apache.httpd.plist

위와 같은 명령으로 기본 설치되어있는 아파치를 더이상 실행되지 않도록 변경할 수 있습니다. 이번에는 새로운 새로운 아파치 2.4 버전을 Homebrew를 이용하여 설치한 뒤 서비스로 구동하는 방법을 알아보겠습니다.

$ brew install httpd24 --with-privileged-ports --with-http2
$ sudo brew services start httpd24

간단하게 설치가 완료되었습니다. 위에서 보여지는 –with-privileged-ports 옵션은 1024 이하의 낮은 숫자의 시스템 포트를 사용하기 위해서 사용하는 옵션입니다. 이 옵션을 사용하여 80번 포트를 사용할 수 있으며 8080과 같은 포트를 사용하실 경우에는 굳이 이 옵션은 필요없습니다. 80번 포트를 사용하여 구동할 경우 보실 수 있듯이 brew services 명령 앞에 sudo를 붙이는것을 볼 수 있습니다.

아파치가 정상적으로 설치되었다면 사용하시는 브라우저에서 localhost로 접속하여 다음과 같은 화면이 뜨는것을 확인하실 수 있습니다.

install-apm-on-macos-sierra01

PHP 설치하기

이제 PHP를 설치해 보도록 하겠습니다. 선호하는 PHP 버전은 다양하게 있겠지만 저의 경우는 5.6을 설치해보도록 하겠습니다. brew search php 명령을 사용하여 사용가능한 PHP 버전을 검색해볼 수 있습니다. 이글을 쓰는 시점에 사용가능한 버전은 5.5 / 5.6 / 7.0 / 7.1 입니다.

$ brew install php56 --with-apache

조금 빌드 시간이 소요되겠지만 설치가 완료되면 바로 사용할 수 있는건 아니고 아파치에 몇가지 설정을 추가해 주어야 합니다.

Apache에 PHP 설정 추가하기

아파치 설정파일은 2.4 버전의 경우에는 /usr/local/etc/apache2/2.4 에서 찾을 수 있습니다. 본인이 선호하시는 텍스트 에디터를 사용하여 /usr/local/etc/apache2/2.4/httpd.conf 를 열어서 다음의 내용을 찾아봅니다.

<IfModule dir_module>
    DirectoryIndex index.html
</IfModule>

위의 내용을 찾으셨다면 다음과 같이 변경을 해주시기 바랍니다. DirectoryIndex 에 값을 추가하였고 FilesMatch 항목을 추가하였습니다.

<IfModule dir_module>
    DirectoryIndex index.php index.html
</IfModule>

<FilesMatch \.php

gt; SetHandler application/x-httpd-php </FilesMatch>

이제 아파치를 재시작 하겠습니다.

$ sudo brew services restart httpd24
Password:
Stopping `httpd24`... (might take a while)
==> Successfully stopped `httpd24` (label: homebrew.mxcl.httpd24)
==> Successfully started `httpd24` (label: homebrew.mxcl.httpd24)

이제 PHP가 정상적으로 구동중인지 확인을 해봐야겠지요. 다음과 같이 PHP 테스트 결과를 출력하는 페이지를 만들어 줍니다. 여기서 알 수 있듯이 기본적으로 아파치 기본 도큐먼트 디렉토리는 /usr/local/var/www/htdocs 입니다.

$ echo "<?php phpinfo();" > /usr/local/var/www/htdocs/index.php

이제 다시 한번 브라우저에서 localhost에 접속해보도록 하겠습니다.

install-apm-on-macos-sierra02

정상적으로 phpinfo가 실행된것을 볼 수 있습니다. 조금 밑으로 내려보시면 MySQL(MariaDB) 역시 정상적으로 연동이 되어있는것을 확인해 볼 수 있습니다.

install-apm-on-macos-sierra03

다 좋은데 date 쪽 항목에서 다음과 같은 경고가 뜨고 있는것을 볼 수 있습니다.

install-apm-on-macos-sierra04

시스템의 타임존 설정이 제대로 되어있지 않다는것인데요. 이렇게 된 김에 이부분의 설정도 해보도록 하겠습니다. php.ini 파일을 수정해야 하는데 /usr/local/etc/php/5.6 안에 있습니다. Date 항목을 찾아서 date.timezone의 내용이 주석처리 되어있는데 다음과 같이 수정해주시면 됩니다.

[Date]
; Defines the default timezone used by the date functions
; http://php.net/date.timezone
date.timezone = Asia/Seoul

이제 준비는 완료되었고 마지막으로 한번 더 아파치를 재시작 해주시면 됩니다.

$ sudo brew services restart httpd24
Password:
Stopping `httpd24`... (might take a while)
==> Successfully stopped `httpd24` (label: homebrew.mxcl.httpd24)
==> Successfully started `httpd24` (label: homebrew.mxcl.httpd24)

Spring Boot에서 손쉽게 MySQL 사용하기

spring_title

프로젝트를 시작하기에 앞서 어떤 언어를 사용할지 어떤 프레임웍, 라이브러리를 사용할지 고민하게 됩니다. 저도 지금까지 이런 고민을 할때마다 큰 규모의 프로젝트는 Spring Framework를 사용하고 빠른 속도의 개발이 필요할때는 PHP를 선택했었습니다. 하지만 Spring Boot를 접하면서 많은 생각이 변하게 되었습니다. 이제는 빠른 개발이 필요할때는 Spring Boot를 사용하면 되겠다는 생각이 듭니다. Rails와 비교할때 아직 많은 부분 부족하다고 생각되지만 Spring의 그 무게감을 잘 유지하고 있으면서도 말도안되게 개발이 편해진것 같습니다. Play의 도입도 고민을 많이 해봤었지만 기존의 Spring에 익숙하시다면 해방된 느낌으로 편하게 사용하실 수 있다고 생각합니다.

이번에는 MySQL(MariaDB) 데이터베이스를 활용하는 프로젝트를 얼마나 손쉽게 구현할 수 있는지 알아보겠습니다. 이 프로젝트는 기존에 작성했던 [Gradle 기반 Spring Boot 프로젝트 구축하기]의 소스코드를 개선해 나가는 방향으로 진행하겠습니다. 우선 링크의 글을 먼저 읽어보시길 권장합니다.

먼저 build.gradle의 내용을 다음과 같이 수정합니다. 하는김에 커넥션풀도 구성해 보겠습니다.

dependencies {
  ...
  compile("org.springframework:spring-jdbc:+")
  compile("commons-dbcp:commons-dbcp:1.4")
  compile("mysql:mysql-connector-java:5.0.8")
}

프로젝트의 진행에 앞서 DB를 사용하는 프로젝트이므로 테스트용 DB와 테이블을 생성해 보겠습니다.

CREATE TABLE `blogs` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `content` text,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

이번엔 데이터베이스 설정파일을 만들어야 합니다. application.properties라는 파일을 프로젝트의 루트 또는 src/main/resources 디렉토리안에 넣어주시면 Spring Boot 구동시에 자동으로 읽어들여 프로젝트에 적용하게 됩니다.

spring.application.name=HelloSpringBoot
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost/theeye?autoReconnect=true&useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=
spring.datasource.maxIdle=20
spring.datasource.maxActive=400
spring.datasource.maxWait=10000
spring.datasource.validationQuery=SELECT 1

위의 내용은 Java 개발자분들께 익숙한 내용일테니 설명은 생략하도록 하겠습니다. 다음은 DAO(모델) 클래스를 만들어 보겠습니다.

@Repository
public class BlogDao {

    @Autowired
    private JdbcTemplate template;

    public void insertNewContent(String content) {
        template.update("INSERT INTO blogs(content) VALUES(?)", content);
    }
}

이제 컨트롤러에서 이 DAO를 호출해 보겠습니다.

@RestController
public class HelloController {

    @Autowired
    private BlogDao blogDao;

    @RequestMapping("/")
    public String index() {
        blogDao.insertNewContent("Hello Spring?");
        return "Greetings from Spring Boot!";
    }

}

여기까지가 끝입니다… 서버를 구동하고 페이지를 열어보시면 정상적으로 DB에 “Hello Spring?”이 저장된것을 확인할 수 있습니다. 느끼셨겠지만 DataSource를 생성하고 Connection Pool을 구축하는 과정이 생략되었습니다. build.gradle 설정에서 spring-jdbc를 추가해주기만 했는데 application.properties의 내용을 토대로 이러한 작업들을 자동화 해줍니다. 그래서 단지 JdbcTemplate을 @Autowired로 엮어줌으로써 DB를 사용할 수 있게 됩니다.

실제 서비스 환경에서 오류가 날경우

개발 환경에서는 위의 과정을 통해 사용이 정상적으로 되었습니다만 이상하게 실제 환경(java -jar로 실행)에서는 MySQL Connector를 로드하지 못하는 에러를 보였습니다. 다음과 같은 에러입니다.

org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is org.apache.commons.dbcp.SQLNestedException: Cannot load JDBC driver class 'com.mysql.jdbc.Driver'
        at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:80)
        at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:628)
        at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:693)
        at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:725)
        at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:735)
        at org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:803)
        at net.loungechat.server.database.UserDao.findUserByAccessToken(UserDao.java:25)
        at net.loungechat.server.processor.ChatServiceProcessor.authorize(ChatServiceProcessor.java:67)
        at net.loungechat.thrift.ChatService$Processor$authorize.getResult(ChatService.java:964)
        at net.loungechat.thrift.ChatService$Processor$authorize.getResult(ChatService.java:948)
        at org.apache.thrift.ProcessFunction.process(ProcessFunction.java:39)
        at org.apache.thrift.TBaseProcessor.process(TBaseProcessor.java:39)
        at org.apache.thrift.server.TThreadPoolServer$WorkerProcess.run(TThreadPoolServer.java:225)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
        at java.lang.Thread.run(Thread.java:744)
Caused by: org.apache.commons.dbcp.SQLNestedException: Cannot load JDBC driver class 'com.mysql.jdbc.Driver'
        at org.apache.commons.dbcp.BasicDataSource.createConnectionFactory(BasicDataSource.java:1429)
        at org.apache.commons.dbcp.BasicDataSource.createDataSource(BasicDataSource.java:1371)
        at org.apache.commons.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044)
        at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:111)
        at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:77)
        ... 15 common frames omitted
Caused by: java.lang.ClassNotFoundException: com.mysql.jdbc.Driver
        at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
        at org.springframework.boot.loader.LaunchedURLClassLoader.doLoadClass(LaunchedURLClassLoader.java:133)
        at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:103)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
        at org.apache.commons.dbcp.BasicDataSource.createConnectionFactory(BasicDataSource.java:1420)
        ... 19 common frames omitted

이 문제를 해결하기 위해 다양한 시도를 해보았지만 Gradle을 이용한 MySQL 원격 저장소 의존성 등록을 하지 않고 직접 라이브러리 파일을 다운받아 등록하는 방법으로 문제를 해결할 수 있었습니다.

[Download Connector/J]에서 적당한 라이브러리를 다운로드합니다. 신기하게도 저의 경우 최신버전을 다운받아 사용하면 오류가 발생했습니다. 그래서 5.0.8 버전을 사용하였습니다.

mysql_libs_dir

프로젝트의 루트에 libs 디렉토리를 생성하고 그안에 다운로드 받은 jar 파일을 넣어두었습니다. 이번엔 build.gradle의 설정을 변경하겠습니다.

dependencies {
    compile("org.springframework.boot:spring-boot-starter-web:1.0.0.RC1")
    compile("org.springframework:spring-jdbc:+")
    compile("commons-dbcp:commons-dbcp:1.4")
    //compile("mysql:mysql-connector-java:5.0.8")
    compile name: 'mysql-connector-java-5.0.8-bin'
    compile fileTree(dir: 'libs', includes: ['*.jar'])
}

우선 시도한 부분은 fileTree설정을 이용하여 libs 디렉토리안의 *.jar를 모두 읽어들이게 한것인데 신기하게 이것도 정상적으로 되지 않는 경우가 발생하였습니다. 그래서 compile name: 설정을 이용하여 정확한 파일명(.jar 확장자 제외)을 적어주면 됩니다.

이렇게 하므로써 환경을 타지 않고 DB를 활용할 수 있는 프로젝트의 구성이 완료되었습니다. 아마 이부분은 버그이지 않을까 생각되네요. 제작한 소스를 첨부하겠습니다. [HelloSpringBootWithMySQL]