반응형

https://github.com/woowacourse-precourse/java-baseball-6/pull/1863

 

[숫자 야구 게임] 이상수 미션 제출합니다. by tkdtn4657 · Pull Request #1863 · woowacourse-precourse/java-baseba

 

github.com

 

첫번째로 README.md 파일을 통해 구현할 기능 목록을 정리하는 부분에서 설계를 어떻게 해야할지 미리 고민해봐야하는데, 해당 내용을 작성 후 설계하는 과정에서 README의 대략적인 흐름만 있을 뿐 정확하게 작성하지 못했다.

다음 주차에서는 README를 작성할 때부터 어떻게 설계해야할지 좀 더 구체적으로 생각하고 설계하는 습관을 길러보아야겠다.

코드를 구현하면서는 두가지 큰 이슈가 있었는데, 첫 구현은 쉬웠지만 이 코드를 리팩토링하려고 보니 어느 부분을 손대야할지 도무지 감이 잡히지 않았고, 블로그의 글도 정리가 잘 되어서 좋긴 하지만 전체적인 구상능력이 부족했던 내게는 블로그로는 완벽히 해낼 수 없다고 판단되었고, 급하게 객체지향의 사실과 오해를 구매 후 읽기 시작했다.

해당 책에서는 설계적 관점에서 객체는 각자의 책임을 갖고있어야 한다고 말을 했고, 이를 RDD(Responsibility Driven Design)방식이라 설명했다. 이 방식은 클래스간의 협력은 서로 메시지만 던져줄 뿐 상대에게 내부로직은 알아서 하도록 맡겨야한다는 부분을 강조했고, 이것이 캡슐화와 큰 연관이 있다고 했다. 때문에 해당 로직을 검증 해보았을 때 Baseball객체는 입력값을 받아서 그것을 변환하는 것이 아닌 변환만 해주는 클래스가 필요하단 것이 생각났고, 이를 통해  리팩토링 과정에서 InputLineConverter클래스 생성 후 입력받은 값을 검증해주는 IllegalCheck클래스에 메시지를 넘겨서 이상없다는 확인을 받고 해당 값을 컨버트하는 등 서로 로직을 진행하되 다른 곳에 협력을 얻을 때는 메시지만 던질 수 있게하는 역할을 할 수 있도록 구현을 완료했다.

 

이후 추가리팩토링 과정에서는 자바의 코드를 8버전에서 사용하는 Ramda, Stream형태로 변경하려고 시도해보았고 주요로직 하나를 변경했다.

public void initRandomNumberList() {

        answerNumberList = new ArrayList<>();
        answerNumberList.add(Randoms.pickNumberInRange(1, 9));

        while (answerNumberList.size() < 3) {

            int nowNumber = Randoms.pickNumberInRange(1, 9);
            int isDupleNumber = (int) answerNumberList.stream()
                    .filter(n -> n == nowNumber)
                    .count();

            if (isDupleNumber == 0) {
                answerNumberList.add(nowNumber);
            }
        }
    }

 

반응형

'공부 > Java' 카테고리의 다른 글

자바의정석 ch14 람다와 스트림  (0) 2023.09.18
BufferedReader, StringTokenizer  (0) 2023.01.14
nextInt()입력 후 nextLine()사용 시 주의점  (0) 2022.12.18
반응형

설치

msi인스톨러 책에서 알려준 사이트를 통해 진행

https://dev.mysql.com/downloads/mysql/

해당 버전으로 진행

msi파일로 설치 진행 시에는 c/Program Files/MySQL 폴더에 저장된다.

서버 설정 my.cnf(my.ini)


일반적으로 MySQL의 서버는 하나의 설정파일만 사용한다.

리눅스를 포함한 유닉스 계열에서는 my.cnf 파일명을 사용하고,

윈도우 계열에서는 my.ini라는 이름을 사용한다.

**my.ini**의 위치는 보통의 경우 MySQL의 basedir 하위에 들어가 있다.

하지만 msi파일로 설치를 했을 경우는 다른 위치에 설치되는데

msi상의 경로로는

C:\\ProgramData\\MySQL\\MySQL Server 8.0

해당 위치고 만약 여기도 없다면 아래의 명령어를 통해서 지정되어있는 위치를 모두 찾아봐야 한다.

show variables like '%dir';

또는

services.msc에 있는 실행설정 경로를 통해서도 확인이 가능하다

"C:\Program Files\MySQL\MySQL Server 8.0\bin\mysqld.exe" --defaults-file="C:\ProgramData\MySQL\MySQL Server 8.0\my.ini" MySQL80

First Login


MySQL shell

명령어 : \connect —mysql root@localhost:3306

\sql을 통해 sql명령어로 변경이 가능

MySQL CommandLine Client

root계정으로 즉시접속됨

MySQL 시스템 변수의 특징


시스템 변수는 상당히 많아서 다 나열할 수는 없지만 변경이 필요할 때 어느 부분에서 속성 변경을 해야하는지 정확히 알아두고 진행해야 할 필요가 있기에 기억을 해둬야한다.

해당 refence DOC : https://dev.mysql.com/doc/refman/8.0/en/server-system-variable-reference.html

또, 해당 표에 적혀있는 5가지 속성의 의미는 다음과 같다.

  • Cmd-Line : MySQL 서버의 명령행 인자로 설정될 수 있는지의 여부
  • Option file : my.cnf(my.ini)로 제어할 수 있는지의 여부
  • System Var : 시스템 변수인지 아닌지(언더바 _ 의 여부)
  • Var Scope : 시스템 변수의 적용범위
  • Dynamic : 동적인지 정적인지 구분

글로벌 변수와 세션 변수


MySQL의 시스템 변수의 종류

Global

하나의 MySQL 서버 인스턴스에서 전체적으로 영향을 미치는 시스템 변수를 의미 주로 MySQL 서버 자체에 대한 설정의 경우가 많다.

대표적인 글로벌 변수로는 innodb_buffer_pool_size(MySQL db엔진 버퍼크기)

Session

각 클라이언트가 MySQL서버에 접속할 때 기본으로 부여하는 옵션의 기본값을 제어하는 데 사용된다.

세션 변수를 이렇게 지정해서 사용하는 이유는 클라이언트별로 원하는 설정방식이 있을 때 정하는데, 대표적인 예시로 autocommit 변수가 있다.

이 변수를 ON으로 설정해두면 해당 서버에 접속하는 모든 커넥션은 기본으로 autocommit으로 되어있지만, 개별 클라이언트 커넥션의 설정에 따라 해당 변수를 OFF설정해 자동 커밋을 비활성화 할 수 있다. 또 이렇게 전역에도 되어있고, 세션에도 개별설정이 가능한 경우

VarScope의 범위가 Both인 것이다.

정적 변수와 동적 변수

MySQL 서버의 시스템 변수는 MySQL 서버가 기동중인 상태에서 변경 가능한지에 따라 동적 변수와 정적 변수로 구분된다.

디스크에 저장돼 있는 설정파일(my.cnf or my.ini)을 변경하는 경우(정적변수)

이미 기동 중인 MySQL 서버의 메모리에 있는 MySQL서버의 시스템 변수를 변경하는 경우로 구분할 수 있다.

이 때 set을 통해서 변수명을 변경할 수도 있고, 변수명을 정확히 모른다면 SQL문장형식으로 패턴검색을 할 수도 있다.

검색

SHOW GLOBAL VARIABLES LIKE ‘%max_connections%’;

변경

SET GLOBAL max_connections=152;

MySQL 8.0 이전

SET을 통해 시스템 변수값을 변경해도 해당 mysql인스턴스에만 적용되기 때문에 영구적용을 하려면 my.ini파일의 직접변경이 필요하다

MySQL 8.0 이상

SET PERSIST명령을 이용하면 인스턴스와 my.시스템파일도 같이 변경된다.

시스템 변수의 범위가 Both라면 글로벌 시스템 변수의 값을 변경해도 값이 변하지 않는다

SET PERSIST

MySQL 동적변수의 경우 SET GLOBAL로 시스템변수를 즉시 적용할 수 있다.

하지만 이는 MySQL이 실행되어있는 이번 인스턴스에서만 적용이 되기 때문에 SQL을 종료했다가 재실행 하게되면 SET으로 변경된 값이 원래대로 돌아온다.

이 이유는 my.ini의 초기설정이 안되어있기 때문이고, 이 값은 SET으로 변경되지 않았기 때문이다.

이를 해결하기 위해 MySQL 8.0부터 SET PERSIST명령으로 시스템 변수를 변경했을 때 시스템설정파일도 변경한 것과 동일한 효과를 낼 수 있게 되었는데, 이 이유는 my.cnf파일이 바뀌지 않고, mysqld-auto.cnf에 변경된 내용을 추가기록하고 MySQL이 실행될 때 my.cnf 포함 mysqld-auto.cnf파일도 읽어들이기 때문이다.

3. 사용자 및 권한


사용자 식별

MySQL은 접속 지점과 아이디가 같이 언급이된다.

ex) svd_id@127.0.0.1

만약 해당 컴퓨터 뿐만이 아니라 다른 컴퓨터에서도 접속이 가능하게 생성하려면

호스트부분을 우측과 같이 해줘야한다. xxx@%

또 만약, 하나의 dbms에 xxx@%와 xxx@127.0.0.1이 같이 있다면 좁은 범위인 127.0.0.1이 먼저 선택되어 접속이 된다.

시스템 계정과 일반 계정

일반계정을 관리, 데이터베이스 서버 관리 등과 같은 중요작업(DBA가 관리하는 것)을 하는 시스템계정과 일반 응용프로그램을 확인하거나 개발자들이 활용하는 일반 계정으로 나뉘어져 있다.

구체적인 시스템계정의 역할

  • 계정 관리(계정 생성 및 삭제, 계정의 권한 부여 및 제거)
  • 다른 세션(Connection)또는 그 세션에서 실행 중인 쿼리를 강제 종료
  • 스토어드 프로그램 생성 시 DEFINER를 타 사용자로 설정

계정 생성

MySQL 8.0버전부터는 계정생성을 GRANT가 아닌, CREATE USER명령을 통해, 권한 부여는 GRANT 명령을 통해 실행되도록 구분했다.

첫 계정 생성시에 설정되는 옵션으로는

  • 계정의 인증 방식과 비밀번호
  • 비밀번호 관련 옵션(유효 기간, 이력 개수, 비밀번호 재사용 불가기간)
  • 기본 역할
  • SSL 옵션
  • 계정 잠금 여부

등이 있으며, 작성예시는 다음과 같다

CREATE USER 'user'@'%'
IDENTIFIED WITH 'mysql_native_password' BY ' 'password'
REQUIRE NONE
PASSWORD EXPIRE INTERVAL 30 DAY
ACCOUNT UNLOCK
PASSWORD HISTORY DEFAULT
PASSWORD REQUIRE CURRENT DEFAULT;

위 옵션을 하나하나 보자면

IDENTIFIED WITH

사용자 인증 방식과 비밀번호를 설정 방식에는 4가지가 대표적

  • Native Pluggable Authentication : 비밀번호 해시 SHA-1알고리즘을 통한 값 저장
  • Caching SHA-2 Pluggable Authentication : SHA-2(256비트)알고리즘을 통한 값 저장
  • PAM Pluggable Authentication : 유닉스나 리눅스 패스워드 또는 LDAP같은 외부 인증을 사용할 수 있게 해주는 인증 방식으로, MySQL 엔터프라이즈 에디션에서만 사용 가능
  • LDAP Pluggable Authentication : LDAP를 이용한 외부인증방식 이또한 MySQL 엔터프라이즈에서만 사용 가능

기본 인증방식은 Caching SHA-2 Authentication이며, 이는 SSL/TLS 또는 RSA 키페어를 필요로 하기 때문에 혹시나 Native Pluggable 인증방식으로 바꿀 때는 추가 명령어가 필요하다.

선택적으로 바꾸는 이유 : 암호화 및 보안에는 SHA-2가 더 좋지만 , 비밀번호 인증 과정에서 해시함수 확인을 위해 CPU의 자원을 많이 소모하기 때문

REQUIRE

MySQL 서버에 접속할 때 SSL/TLS의 사용 여부

PASSWORD EXPIRE

비밀번호의 유효기간 설정 PASSWORD EXPIRE 절에 설정 가능한 옵션은 다음과 같다.

  • EXPIRE : 계정 생성과 동시에 비밀번호의 만료 처리
  • NEVER : 비밀번호 만료기간 없음
  • DEFAULT : default_password_lifetime : 시스템 변수에 저장된 기간
  • INTERVAL n DAY : 비밀번호의 유효기간을 오늘부터 n일자로 설정

PASSWORD HISTORY

한번 사용했던 비밀번호를 재사용 못하게 하는 옵션

  • PASSWORD HISTORY DEFAULT : 시스템변수에 저장된 개수만큼
  • n : 최근 n개까지만 비밀번호의 이력을 저장해 이력에 남아있는 비밀번호만 불가능

PASSWORD REUSE INTERVAL

한번 사용했던 비밀번호의 재사용 금지기간을 설정하는 옵션

PASSWORD REQUIRE

비밀번호가 만료되었을 때 현재 비밀번호를 필요로 할지 말지를 결정

ACCOUNT LOCK / UNLOCK

계정 생성 시, ALTER USER 명령을 사용해 계정 정보를 변경할 때 계정을 잠글지 사용가능하게할지 여부 설정

비밀번호 관리

암호설정을 고수준으로 하도록 변경할 수도 있다.

고수준 암호 옵션은 세가지로 나뉜다.

  • LOW : 비밀번호의 길이만 검증
  • MEDIUM : 비밀번호의 길이를 검증, 숫자 대소문자, 특수문자 배합 검증
  • STRONG : MEDIUM의 검증 포함하여 금칙어 검증

등이 있다.

권한

데이터베이스나 테이블과 객체에 적용되는 권한을 글로벌 권한이라고 하며, 데이터베이스나 테이블을 제어하는 데 필요한 권한을 객체 권한이라고 한다.

객체 권한은 GRANT 명령으로 부여할 때 특정 객체를 명시해야 하며, 글로벌 권한은 명시하지 말아야 한다.

예외적인 권한 부여 방법으로는 ALL 또는 ALL PRIVILEGES가 있는데, 이는 글로벌과 객체 두가지 용도 모두 사용될 수 있는데, 객체에 ALL이 부며되면 해당 객체에 적용될 수 있는 모든 권한을 부여받는 것이며, 글로벌로 ALL이 사용되면 글로벌 수준에서 가능한 모든 권한을 부여받는다.

역할

MySQL 8.0 버전부터는 권한을 묶어서 역할(ROLE)을 사용할 수 있게 되었다.

CREATE ROLE
role_emp_read,
role_emp_write;

처럼 역할을 정의할 수 있고 이러한 역할에 GRANT를 통해 실제 권한들을 부여해 관리할 수 있다.

GRANT SELECT ON employees.* TO role_emp_read;
GRANT INSERT, UPDATE, DELETE ON employees.* TO role_emp_write;

role_emp_read 객체에는 employees DB의 모든 객체에 SELECT만 가능

관리하는 방법에 따라서 편하게 역할과 권한으로 지정해서 사용하면 될 것 같다.

원본 노션 : https://bitter-jitterbug-b56.notion.site/ch-02-ch-03-634871df343b4de8a332a86b774b5868?pvs=4 

 

ch.02 설치와 설정, ch.03 사용자 및 권한

설치

bitter-jitterbug-b56.notion.site

 

반응형
반응형

최근 팀원과 프로젝트 진행 중 배포 후 구글폼과 함께 배포된 사이트를 피드백받고자 홍보를 했었다.

홍보 후 들어왔던 피드백중 하나였는데,

검색창에 select를 치면 모든 게시글이 나와요.

라는 피드백이였고, 너무 놀란 나머지 최대한 빨리 원인분석을 해보았다. 원인분석 이후 도달한 결론은 “문제없음” 이였다.

이유는 우리의 injection방어로직은 아래와 같은데,

    /**
     * SQL Injection 방어하기 위하여 구현
     */
    private String changeNoSqlInjection(String query) {
        query = SPECIAL_CHARS.matcher(query).replaceAll("");
        for (String s : STRING_SET) {
            if (query.contains(s)) {
                return "";
            }
        }
        return query;
    }

해당 코드에서는 sql문인 select, update, insert, delete의 문자가 들어오면 무조건 빈 문자열을 리턴하기 때문에 “select”를 포함한 문자열을 검색했을 시 빈 문자열로 검색을 돌렸기에 위와 같은 상태가 되었던 것이었기에 문제가 없는 것이다.

하지만 이번 일로 인해 프로젝트를 진행하면서 중요하게 놓치고 있는 것을 다시한번 깨닫게 되었는데, 이유는 아래와 같다.

  • 내가 팀원이 구현한 해당 sql Injection관련 코드를 봤었는데 그냥 그렇구나~ 하고 가볍게 넘긴 것
  • 해당 기능이 구현된 후 직접 테스트해보지 않은 것

평소에 코드리뷰를 하려고 pull request 후에 리뷰, approve하는 형식인데 어느순간부터 아무 생각없이 approve만 하는 습관이 생기는 것 같다 물론 내가 바쁜 것도 바쁜 것이지만, 상대방의 코드를 통해 배울 점도, 부족한 점이 있다면 서로 피드백을 하면서 확인하는 것이 내가 성장하기에 가장 좋을텐데 간과하고 있었던 것이 이렇게 돌아올줄이야… 또, SQL injection이라면 기본적이면서도, 중요한 문제인데 이를 구현된 코드만 보고 가볍게 넘어가고 테스트해보지 않은 것이 문제가 있다고 생각했고, 더 신경써서 테스트를 해봐야 한다고 다시한번 느꼈다.

반응형

+ Recent posts