반응형

이번에 리액트로 쇼핑몰 프로젝트를 연습삼아 만들어보게되면서 닷홈의 무료 호스팅을 신청해서 

그 서버에 빌드한 파일을 올리는 방법으로 호스팅해주었다. 과정은 간단한데 로컬에서 파일을 빌드하고 닷홈서버의 html폴더에 빌드한 파일을 넣어주기만 하면되는거라서 간단하게 자동화가 가능할것같았다. 그래서 github action을 이용해서 업데이트 자동화를 해보기로 했다. 

 

우선 깃허브에 코드를 올려주고 해당 리포지토리로 이동해준다.

리포지토리에서 Actions로가서 simpleworkflow를 검색한다음 configure를 클릭한다.

그러면  생성한 리포지토리의 .github/workflows 경로에 blank.yml 파일을 생성해주게 된다.

이제 이파일에 필요한 동작들을 작성해주면 코드가 푸쉬됐을때 깃허브에서 띄워주는 가상의 컴퓨터에서 입력한 동작들을 수행해준다 

 

내가작성한 코드를 올려줄테니 참고해서 닷홈에 자동화로 올리는 걸 많이 시도해봤으면 좋겠다.

name: CI/CD Pipeline

on:
  push:
    branches:
      - main # 메인 브랜치가 푸쉬될때 동작

jobs:
  build:
    runs-on: ubuntu-latest # 사용할 os 버전 

    steps:
    - name: Check out code
      uses: actions/checkout@v2

    - name: Set up Node.js
      uses: actions/setup-node@v2
      with:
        node-version: '18' # 사용할 Node.js 버전을 설정합니다.

    - name: Install dependencies
      run: npm install # 프로젝트의 의존성 설치 

    - name: Build project
      run: npx vite build # 프로젝트 빌드 명령어를 실행합니다.

    - name: List dist directory contents
      run: ls -al dist # 빌드된 파일의 폴더로 이동 
      
    - name: Install LFTP
      run: |
        sudo apt-get update
        sudo apt-get install -y lftp # ftp 프로그램 설치 

    - name: Transfer files via SCP
      env:
        SERVER_HOST: ${{ secrets.SERVER_HOST }}
        SERVER_USERNAME: ${{ secrets.SERVER_USERNAME }}
        SERVER_PASSWORD: ${{ secrets.SERVER_PASSWORD }}
      run: 
        lftp -u  ${{ secrets.SERVER_USERNAME }},${{ secrets.SERVER_PASSWORD }} ftp://${{ secrets.SERVER_HOST }}:21 -e "
        mirror -R dist /html;
        bye
        "

 

이렇게 설정하면 원격컴퓨터에서 노드를 설치하고 파일의 의존성을 설치한다음 빌드 하고 해당 파일을 원격지의 닷홈서버로 ftp프로토콜로 전송하게되는 코드이다. 

vite를 사용해서 dist 폴더에 저장되는것같은데 build명령어나 의존성을 설치하는명령어는 사용하는 패키지 매니저나 명령어에 따라서 다르기때문에 자신의 환경에 맞춰서 써주면 될것같다. yarn을 이용하는사람은 yarn 명령어를 사용해야할테고.. 

 

닷홈은 ssh 를 열어놓지않기때문에 수작업으로 할때는 빌드한 파일을 파일질라로 닷홈서버에 옮겼는데 이렇게하면 메인 브랜치에 코드가 푸쉬될때 자동으로 빌드한파일을 닷홈의 /html폴더로 이동시켜서 코드만올려도 자동으로 업데이트 되는 효과를 볼수있다. 프로젝트가 복잡해지면 다른 추가적인 과정도 필요하게 될수도있지만 현재로서는 잘 동작하는듯하다.

반응형
반응형

프로젝트를 만들려고 다시 옛날에 만들었던 코드를 보니 디자인이나 구성요소를 가져가기에는 
맘에 들지않는 부분이많아서 디자인이나 기능 부분도 새로 만들어보기로 했다. 

 

참고로 나는 디자인에 소질이 정말 없다고 자부하는데 고르는 색깔마다 별로 좋은 반응을 받은적이없어서 AI툴을 이용해보기로 했다 

 

https://v0.dev/

 

v0 by Vercel

Chat with v0. Generate UI with simple text prompts. Copy, paste, ship.

v0.dev

next.js 를 만들고 vercel 을 만드는 곳에서 개발한 AI툴인데 프롬프트에 필요한 디자인을 입력하면 
해당 컴포넌트와 필요한부분들이 모두 코딩되어서 코드수준으로 만들어준다. 

우선 내가 필요한 페이지를 정리해 보았다. 

가장 먼저 보여지는 index 페이지는 베스트 셀러나 추천하는 상품과 배너가들어갈수있는 페이지로 구성하고 

각각 페이지들은 상품의 카테고리로 들어갈수있는 전형적인 쇼핑몰 형태의 페이지로 구성한다

추가적으로 관리자 페이지가 필요한데 관리자의 index페이지는 세일즈코스트나 잘 팔리는 상품들을 그래프로 조회할수있는 패널형태의 페이지로 구성하고 각각 페이지에 관리자에게 필요한 기능을 넣은 페이지로구현하려고 한다.

 

관리자에게 필요한기능은 구현하면서 계속 추가될것같지만 우선적으로 필요한기능은 

1. 매출액 조회 (월단위 , 일단위 , 지정한 시작 종료일 기준)

2. 상품등록 , 삭제 , 수정 

3. 회원관리

4. 재고관리 

5. 할인또는 프로모션의 관리 

6. 공지 기능 

벌써 꽤 머리가 아플것같은 기능들이있는데 아마 진행하면서 더 늘어날수도있을것같다. 한 번에 다 구현은 할수없으니 하나씩 천천히 구현 해봐야할것같다. 현재로써는 상품에 댓글 기능은 포함하지않는 기획이라 댓글이나 답글은 없어도될듯하다. 사실 최초 기획단계에서 필요한 요소를 모두 정의하고 들어가면 좋겠지만 코딩을 공부하는 입장에서 그렇게까지하면 코드를 공부하는 시간보다 기획에 시간을 너무 쏟게 될것같아서 기능이 추가될수있다는 점만 염두에두고 확장성을 고려하여 코드작업을 해보도록 하겠다. 그쪽이 더 공부가 될것같고... 이전 기획과 새로운 기획에서 충돌이 났을떄의 해결도 좋은 경험이 될것같다.

 

나는 꼭 vite + typesciript + react  연습을하기 위해서 v0.dev로 만들고 react 만 사용하는 환경으로 옮기느라 고생을좀했지만 next.js프레임워크를 사용한다면 v0.dev가 만든 코드를 그대로 프로젝트로 생성해서 사용할수있어서 더 편하게 이용할수있다 .

 

해당 페이지는 빌드해서 호스팅사이트에 올려놓았다. 무료 호스팅이라 비용없이 간단하게 이용할수있어서 좋은것같다

corona456.dothome.co.kr

 

Vite + React + TS

 

corona456.dothome.co.kr

 

v0.dev 로만든 소스코드도 깃허브에 올려놓았으니 설정에 어려움이있다면 참고하자 

https://github.com/cokeholic-kim/Univershop

 

GitHub - cokeholic-kim/Univershop

Contribute to cokeholic-kim/Univershop development by creating an account on GitHub.

github.com

 

반응형
반응형

작년 초에 학원에서 리액트로 쇼핑몰을 만드는 수업을 받았었다.  

다시 리액트를 사용하려고보니 빌드툴도 vite라는 것을 사용하고 상태관리쪽도 새로운 라이브러리가 떠오르고있는듯해서 

다시 연습하는겸 쇼핑몰 프로젝트를 다시만들어보려고한다. 

지금 다니는 회사에서도 리액트 , 타입스크립트 , 넥스트 같은 기술들을 도입하려고 준비하다보니 따라가는 입장에서 연습을 해보고 사용할수있는 api도 회사에서 사용하는 기술인 fastAPI 프레임워크를 통해서 만들어보겠다. 

 

1. 프로젝트 세팅

npm create vite@latest 프로젝트이름

프레임워크와 옵션을 선택할수있게 나올텐데 방향키로 움직여서 원하는 옵션에서 엔터를 누르면 해당 옵션과 프레임워크가 설치된다.

 

나는 리액트 프레임워크에 타입스크립트와 swc를 사용하게 설정해주었다. 

 

이제 vscode로 프로젝트를 열어주고 프로젝트를 실행하기위해 라이브러리를 설치해준다.

npm i

 

라이브러리들이 다 설치되면 잘 실행되는지 확인해본다.

npm run dev

이렇게 running 되었다고 뜰텐데 저 localhost 주소로 들어가보면 페이지가 정상적으로 동작하는지 확인하면 된다.

이렇게하면 리액트 프로젝트를 시작할준비가 끝이났다. 

반응형
반응형

리눅스 공부를 하면서 가장 기본적으로 많이 이용하는 APM 환경을 직접 설치해보면서 이것저것 해보는데 

한 서버상에서 php를 여러 버전 설치하고 각 사용자가 웹호스팅을 할때 필요한 php 버전을 골라서 사용할수있도록 환경을 만들어 보고싶었다.  

 

우선 나는 리눅스는 우분투 환경을 사용하고있고 윈도우상에서 VirualBox로 가상환경을 만들어서 진행했다.

 

PHP를 한번에 여러버전을 설치하고 관리하기위해서 

ondrej/php 라는 ppa를 이용했다. 

리눅스에서는 PersonalPackageArchive라는게 있는데 간단하게정리하면

개인이 배포하는 소프트웨어가 저장된 저장소라고 생각할수있을것같다.

ondrej/php가 여러버전의 php버전을 제공하는 유명한 ppa라고 되어있어서 이용했다.

 

PPA설치 

sudo apt-get update 
sudo apt-get install software-properties-common #ppa를 이용하기 위해설치
sudo add-apt-repository ppa:ondrej/php #다양한 버전의 php를 제공하는 ppa
sudo apt-get update

 

php 설치

sudo apt-get install php7.4 php7.4-fpm php7.4-mysql php7.4-curl php7.4-xml php7.4-gd php7.4-mbstring
sudo apt-get install php8.0 php8.0-fpm php8.0-mysql php8.0-curl php8.0-xml php8.0-gd php8.0-mbstring
sudo apt-get install php8.2 php8.2-fpm php8.2-mysql php8.2-curl php8.2-xml php8.2-gd php8.2-mbstring

 

이렇게 세가지 버전의 php를 라이브러리까지 해서 설치할수있다.  

 

설치후에 루트경로에서 run/php 경로에들어가면 설치된 php의 sock파일과 pid파일을 볼수있다.

cd /run/php
ls -al
합계 12
drwxr-xr-x  2 www-data www-data  180 11월  3 16:07 ./
drwxr-xr-x 38 root     root     1000 11월  3 16:12 ../
lrwxrwxrwx  1 root     root       30 11월  3 16:04 php-fpm.sock -> /etc/alternatives/php-fpm.sock=
-rw-r--r--  1 root     root        5 11월  3 16:05 php7.4-fpm.pid
srw-rw----  1 www-data www-data    0 11월  3 16:05 php7.4-fpm.sock=
-rw-r--r--  1 root     root        5 11월  3 16:06 php8.0-fpm.pid
srw-rw----  1 www-data www-data    0 11월  3 16:06 php8.0-fpm.sock=
-rw-r--r--  1 root     root        5 11월  3 16:07 php8.2-fpm.pid
srw-rw----  1 www-data www-data    0 11월  3 16:07 php8.2-fpm.sock=

 

이렇게 하면 한개의 서버에 여러 버전의 php를 설치할수있다.

반응형
반응형

사이트나 서버를 HTTPS로 호스팅하기위해서는 SSL 인증서가 필요하다.

인증서의 종류나 인증서를 발급해주는기관 또 인증서별로 지원을 해주는기능들이다르다.  

SSL인증서가 무엇인지 알아보자 

 

 

보안인증서

  • 웹 서버와 사용자 pc간에 전송되는 모든 정보를 암호화하여 안전하게 전송하는 디지털 인증서
  • 스니핑으로 인한 개인정보 유출 방지를 위해서 필수적인 보안수단
  • 데이터 변조를 방지

 

인증서중에는 서브도메인이나 멀티도메인 강제암호화를 지원하는 상품이있다.

 

서브도메인 인증서 - 와일드카드 SSL인증서

  • 하나의 인증서로 서브도메인에서도 사용이가능한 인증서 
  • ex) *.example.com 형식으로 모든 서브도메인을 커버한다.

멀티도메인 인증서 - 다중 도메인 인증서

  • 도메인 주소가 서로 다른경우 인증서 하나로 적용할수있는 멀티도메인 인증서.
  • 다중도메인 인증서는 SAN(Subject AlternativeName)필드를 사용하여 다양한 도메인을 지정할수있다.

강제 암호화 - SGC (Server Gated Cryptography)

  • 클라이언트측의 낮은 암호화를 강제적으로 높은bit의 암호화로 올린다
  • 낮은 버전의 브라우저도 상관없이 높은 수준의 암호화를 요구하는 페이지에 접근이 가능하다.
  • 브라우저 종류에 상관없이 접근이 가능해야한다면 해당 상품을 이용해야한다.

보안인증서 인증 절차

  1. csr 생성 Certificate Signing Request : 도메인 이름.회사정보 , 공개키 등을 포함한 CSR파일을 생성
  2. 인증 기관 선택 CA - Certificate Authority
  3. csr제출 : 생성한 CSR파일을 선택한 인증 기관에 제출합니다.
  4. 도메인 소유권 확인  - 이메일인증, 파일인증, dns인증
  5. 인증서 발급 : 인증 기관이 소유권 확인을 완료하면 SSL인증서를 발급합니다.
  6. 인증서 설치 : 웹서버의 설정파일을 수정하여 발급된 인증서를 설치하고, HTTPS 를 활성화한다. 주로 443번포트를 사용합니다.

* https로 도메인에접근하면 포트를 별도로 명시하지않아도 자동으로 서버의 443번포트에 연결된다.

 

인증서가 가진 정보

  • 주체 - 발급된 도메인이름
  • 발급자 - CA정보: 발급기관
  • 서버 공개키
  • TLS 버전 - 암호화 프로토콜
  • 세션정보
  • 검증결과

 

반응형

'공부 임시 저장소' 카테고리의 다른 글

도메인 레코드란? DNS Record  (1) 2024.10.13
도메인이란? DNS  (1) 2024.10.13
스프링 부트 - spring-security-jwt  (0) 2024.06.25
스프링부트 - aws s3파일저장  (0) 2024.06.25
스프링부트 - 파일 저장  (0) 2024.06.24
반응형

DNS Record

도메인 이름에 대한 다양한 정보를 저장하며 클라이언트가 도메인을 입력할때 

이 정보를 받아서 적절한 처리를 한다.

 

일반적으로 사용되는 DNS 레코드에는

A 및 AAAA 레코드,

CNAME,

DNAME 및

ALIAS 레코드,

CAA 레코드,

CERT 레코드,

MX 레코드,

SOA 레코드,

NS 레코드,

PTR 레코드,

SPF 레코드,

SRV 레코드 및 TXT 레코드가 포함됩니다.

이러한 각 레코드는 고유한 기능을 가지고 있으며 각각의 레코드를 이해하는 것은 DNS 시스템이 제대로 작동하는 데 중요한 부분입니다. 

 

A 레코드

  • A레코드는 도메인이름을 직접적으로 서버 아이피 주소와 연결해주는 레코드이다.
  • A레코드에 입력한 도메인주소로 접근하면 IP주소로 연결된다.

 

CNAME - Canonical name

  • 도메인을 별명과 연결한다 . 
  • IP 주소가 유동적으로 변하는 서버의 경우 A 레코드로 작성하면 IP가 변경될때마다 A레코드를 다시 작성해주어야한다.
  • CNAME으로 A레코드의 도메인 주소값을 입력하면 IP값이 변경되어도 레코드를 수정해주지 않아도 접근이 가능하다.

 

예를 들어 CNAME으로 www.soolae-server.shop을 을 작성하고

a레코드는 soolae-server.shop이고 

a레코드에서 가리키는 IP는 유동적으로 변경된다고 했을때 

 

사용자가 www.soolae-server.shop  인 CNAME으로 접근하고

CNAME은 A레코드값과 연결되고

A레코드가 가진 IP값을 가지고 온다.

 

MX레코드 - Mail Exchange Record

  • 여러개의 MX 레코드를 생성할수있고 우선순위를 설정할수있다. Priority
  • 이메일 서버는 우선순위가 낮은 레코드를 사용하려고 시도한다. ex) 우선순위가  1 , 2, 3 인경우 1
  • 메일 전송시 해당 도메인의 mx 레코드로 전송된다.

 

ex)

A레코드에 메일을받을 서버의 IP를 연결해서 생성  / mail.도메인주소

MX레코드를 생성 / 도메인주소 10 mail.도메인주소

 

이렇게 입력하면 해당도메인주소로 이메일이 전송될때 donghyeon@도메인이름

mx레코드를 요청하고 네임서버는 우선순위가높은 mx레코드를 보내주고

연결된 도메인인 mail.도메인 으로 메일이 전송된다. 

그럼 이메일을 mail.도메인 에 연결된 ip주소를 가진 서버에서 처리하게된다.

 

TXT레코드 

  • 텍스트 레코드는 텍스트 문자열을 넣을수있는 레코드 이다.
  • 도메인이나 하위 도메인과 관련된 텍스트 정보를 저장한다.
  • SPF 레코드와 이메일 확인 레코드를 저장할 수 있다. 

 

SPF레코드 - Sender Policy Framework

  • 도메인 에서 이메일을 보낼수있도록 승인된 서버를 나열하는 텍스트 레코드
  • SPF레코드는 TXT레코드를 작성하는 한 방법이며 이메일 인증을 위해서 사용하는 레코드이다.

 

ex)

도메인 / TXT / "v=spf1 mx include:mailplug.com ~all"​

v=spf1 : SPF레코드의 버전

mx : 도메인의 메일 서버를 허용

include:mailplug.com : mailplug.com 에서 메일을 보낼수있는 서버를 포함 / mailplug.com의 spf 정책을 이용한다

~all : 메일전송정책 해당 SPF정책을 따르지않는 이메일을 유사스팸으로 간주하고 수신서버에서 판단하여 스팸으로 분류.

-all : 엄격한거부정책 SPF를 따르지않는 이메일을 모두 거부.

 

SRV레코드

_admin._tcp.soolae-server.shop. 300 IN SRV 1 10 8081 soolae-server.shop.

 인스턴트 메시징 등의 특정 서비스에 대한 포트를 지정

해당 IP주소의 포트 정보도 포함

일부 인터넷 프로토콜은 SRV레코드를 작성해야 동작할수있다.
서비스 지정: SRV 레코드는 특정 서비스(예: SIP, XMPP 등)가 사용하는 포트를 지정합니다.
프로토콜: SRV 레코드는 어떤 프로토콜을 사용하는지를 명시합니다. 예를 들어, `_sip._tcp.example.com`은 SIP 프로토콜을 TCP를 통해 사용하는 서비스임을 나타냅니다.
우선순위 및 가중치: SRV 레코드는 우선순위와 가중치를 설정할 수 있어, 여러 대의 서버가 있을 경우 어떤 서버로 연결할지를 결정하는 데 도움을 줍니다.

 

연결과정

  1. 도메인 쿼리: 클라이언트가 특정 도메인에 대해 SRV 레코드를 조회합니다.
  2. SRV 레코드 응답: SRV 레코드는 해당 서비스에 대한 포트와 IP 주소를 제공합니다.
  3. A 레코드 조회: 만약 SRV 레코드가 도메인 이름을 포함하고 있다면, 클라이언트는 해당 도메인에 대한 A 레코드를 조회하여 실제 IP 주소를 확인합니다.
  4. 서비스 연결: 클라이언트는 SRV 레코드에서 제공된 포트와 A 레코드에서 제공된 IP 주소를 사용하여 해당 서비스에 연결합니다.

DNS SOA레코드 

도메인에 관련된 설정정보 레코드

  • 도메인 영역에대한 중요한 정보를 저장
  • 권한과 관리정보를 포함
  • 네임서버: 해당 도메인의 주요 네임서버 정보를 포함
  • 관리자이메일: 네임서버 관리자 이메일 주소를 나타낸다
  • 시리얼너버: 도메인 설ㅈ엉의 버전을 나타내며 , 업데이트시 증가
  • 리프레시 주기: 주 서버가 보조 서버로 데이터를 갱신하는 주기를 나타낸다.
  • 재시도 주기 : 주서버와 연결 실패시 보조 서버가 재시도하는 주기
  • 만료시간 : 캐시된 정보를 폐기하기 전에 유지하는 시간.
  • 최소 TTL값: 캐시된 DNS레코드의 기본 유효 시간을 설정

TTL - Time To Live

패킷이 라우터에 의해 폐기될때 까지 네트워크 내부에 존재하도록 설정된 시간 또는 홉(Hop) 의 양을 나타낸다.

- 홉(Hop) : 패킷이 네트워크의 장비를 지나갈때 마다 홉이 하나씩 증가, 홉을 통과할때마다 패킷의 생존 시간이 감소

 

DNS TTL

  1. dns레코드가 생성될때 TTL값을 설정한다.
  2. 클라이언트가 dns서버에 도메인이름을 조회할때 해당 dns서버는 요청된 레코드를 찾는다.
  3. dns서버는 레코드를 반환할때 TTL값을 함께 제공한다. 이 TTL값은 클라이언트나 다른 dns서버가 해당 레코드를 얼마나 오랫동안 저장할지를 결정한다.

 

 

 

 

 

반응형

'공부 임시 저장소' 카테고리의 다른 글

SSL 보안인증서란?  (0) 2024.10.13
도메인이란? DNS  (1) 2024.10.13
스프링 부트 - spring-security-jwt  (0) 2024.06.25
스프링부트 - aws s3파일저장  (0) 2024.06.25
스프링부트 - 파일 저장  (0) 2024.06.24
반응형

 

도메인

도메인은 영문이나 한글로된 인터넷주소이다. 

네이버나 구글 같은 웹사이트에 접근하기위해서는 아이피 주소를 알아야하는데. 
사용자가 이 아이피주소를 모두 기억할수없기때문에 문자로된 도메인주소를 연결해서 
도메인주소로접근했을때 해당하는 아이피주소를 보내줘서 웹서버에 접근이 가능해진다.

 

도메인 형식은 이름, 확장자, 서브도메인 으로 구성된다 

예를 들어 내가 구매한도메인인 soolae-server.shop 은 

soolae-server 가 이름이고 

.shop이 확장자이다 확장자는 TLD 라고도 부르며 TopLevelDomain의 약어이다.

 

 

서브도메인

이렇게 하나의 도메인이있으면 도메인의 앞에 호스트네임을 붙여서 용도별로 도메인을 사용할수있다

soolae-server.shop 이라는 도메인으로 메일서버를 호스팅한다면

mail.soolae-server.shop

블로그를 호스팅한다면 

blog.soolae-server.shop 으로

하나의 도메인으로 역할에 따라 다양하게 사용할수있다.

하나의 도메인값에 여러 하위도메인이 속해있다.

 

DNS

인터넷 에서 도메인 이름과 IP 주소를 변환해주는 시스템

사용자가 웹사이트의 주소를 입력하면 DNS 가 IP 주소로 변환하여 해당 서버에 접근하게 해준다.

 

네임서버

네임서버는 DNS시스템의 일부로 특정도메인의 DNS레코드를 저장하고 관리하는 서버

네임서버는 도메인 이름을 IP주소로 변환하는 작업을 담당한다.

 

반응형

'공부 임시 저장소' 카테고리의 다른 글

SSL 보안인증서란?  (0) 2024.10.13
도메인 레코드란? DNS Record  (1) 2024.10.13
스프링 부트 - spring-security-jwt  (0) 2024.06.25
스프링부트 - aws s3파일저장  (0) 2024.06.25
스프링부트 - 파일 저장  (0) 2024.06.24
반응형

4일차 과제는 이 코드를 리팩토링하고

Solid에 대해서 설명하는 내용이다.

우선은 리팩토링부터해보자

리팩토링

public boolean validateOrder(Order order) {
    if (order.getItems().size() == 0) {
        log.info("주문 항목이 없습니다.");
        return false;
    } else {
        if (order.getTotalPrice() > 0) {
            if (!order.hasCustomerInfo()) {
                log.info("사용자 정보가 없습니다.");
                return false;
            } else {
                return true;
            }
        } else if (!(order.getTotalPrice() > 0)) {
            log.info("올바르지 않은 총 가격입니다.");
            return false;
        }
    }
    return true;
}

 

1. if- else 제거

public boolean validateOrder(Order order) {
    if (order.getItems().size() == 0) {
        log.info("주문 항목이 없습니다.");
        return false;
    }
    if (order.getTotalPrice() > 0) {
        if (!order.hasCustomerInfo()) {
            log.info("사용자 정보가 없습니다.");
            return false;
        }
            return true;
    }
    if (!(order.getTotalPrice() > 0)) {
        log.info("올바르지 않은 총 가격입니다.");
        return false;
    }
    return true;
}


위 코드에서 if - else 문을 모두 if 문으로 변경해줬다.

이미 코드내에서 조건문에 해당하면 return으로 반환하면서 함수를 끝내기때문에 else로 분기문을 나눌필요가 없었다.

  • 조건1 - 주문아이템이 0개이면 false
  • 조건2 - 주문한상품금액이 0  이상인 경우
    • 조건2-1 - 주문에 사용자정보가없는경우 false
    • 조건2-2 - 사용자정보가있으면 true를 리턴
  • 조건3 - 주문금액이 0원보다적은경우 false

조건을 정리하다보니 기본 리턴을 true로 잡고 각 예외상황에 대한 처리만 조건으로 해주면 될것같다. 

 

 

부정연산자 제거

public boolean validateOrder(Order order) {
    if (order.getItems().size() == 0) {
        log.info("주문 항목이 없습니다.");
        return false;
    }
    if (order.getTotalPrice() > 0) {
        if (orderHasNotCustomerInfo(order)) {
            log.info("사용자 정보가 없습니다.");
            return false;
        }
    }
    if (order.getTotalPrice() < 0) {
        log.info("올바르지 않은 총 가격입니다.");
        return false;
    }
    return true;
}

private boolean orderHasNotCustomerInfo(Order order) {
    return !order.hasCustomerInfo();
}

 

! 연산자로 한번 뒤집어서 생각하게되던 연산자를 제거하고 메서드로 추출해서 코드의 의미를 좀 더 빠르고 명확하게 이해할수있게만들었다. 

 

  • !(order.getTotalPrice( ) > 0) 부분은 - totalPrice가 0이상이 아닌경우 인데 부등호를 뒤집어서 부정연산자를 빼주었다 
  • !order.hasCustomerInfo( ) 는 order가 고객정보를 가졌는지를 boolean으로 리턴하는 메서드인것같은데 우선은 여기서 메서드로 추출해서 부정을하는 메서드를 만들었다. 실제 부분에서는 해당 Order클래스에 고객정보가없을때 true를 리턴해주는 메서드를 만드는것도 좋을것같다.

메서드로 추출해서 추상화레벨 올리기

    public boolean validateOrder(Order order) {
        if (order.doesNotHaveItem()) {
            log.info("주문 항목이 없습니다.");
            return false;
        }

        if (totalPriceBiggerThanZero(order)) { //토탈주문금액이 0원이 아닌데 사용자정보가없는경우 - 정상주문인데 사용자 정보가없는경우
            if (orderHasNotCustomerInfo(order)) {
                log.info("사용자 정보가 없습니다.");
                return false;
            }
        }

        if (totalPriceLowerThanZero(order)) {
            log.info("올바르지 않은 총 가격입니다.");
            return false;
        }
        return true;
    }

    private static boolean totalPriceLowerThanZero(Order order) {
        return order.getTotalPrice() < 0;
    }

    private static boolean totalPriceBiggerThanZero(Order order) {
        return order.getTotalPrice() > 0;
    }

    private boolean orderHasNotCustomerInfo(Order order) {
        return !order.hasCustomerInfo();
    }

 

이번에는 조건문에들어가는 조건들의 추상화 레벨을 올려주었다.

함수의 이름만으로 어떤걸 검사해서 어떤 값을 리턴하는지 파악이 가능해졌다. 

이제 중첩된 조건문을 수정하려고하는데 2가지 조건으로 검사한후 false를 리턴해주고있다

 

1. 토탈주문금액이 0원이 아닌경우

2. 주문한사용자의 정보가 없는경우

개인 적인 생각이지만 주문한 사용자의 정보가없는경우는 무조건 false가 나와야하지않을까 라는 생각이들었다. 

 

case1 - 주문을 한 금액은 있는데 주문자 정보가없는경우

case2 - 주문을 한 금액은 있는데 주문자 정보가 있는경우 ok

 - 주문한 금액이없는경우는 아래의 totalPriceLowerThanZero에서 걸러진다.

 

케이스를 봤을때 주문한금액으로 검증할 필요가 없을것같다 totalPriceLowerThanZero를 위로올리면 주문금액이 0원 이하인 케이스는 모두 걸러지고 아래에는 토탈주문금액이 0원이상인 케이스만 남아있게된다.

 

조건문의 순서를 변경해서 중첩된 조건문제거

public boolean validateOrder(Order order) {
        if (order.doesNotHaveItem()) {
            log.info("주문 항목이 없습니다.");
            return false;
        }

        if (totalPriceLowerThanZero(order)) {
            log.info("올바르지 않은 총 가격입니다.");
            return false;
        }

        if (orderHasNotCustomerInfo(order)) {
            log.info("사용자 정보가 없습니다.");
            return false;
        }
        return true;
    }

    private static boolean totalPriceLowerThanZero(Order order) {
        return order.getTotalPrice() < 0;
    }

    private static boolean totalPriceBiggerThanZero(Order order) {
        return order.getTotalPrice() > 0;
    }

    private boolean orderHasNotCustomerInfo(Order order) {
        return !order.hasCustomerInfo();
    }

 

최종적으로 이렇게 리팩토링 해주었다. 

각 조건문마다 검증하는 항목이 달라서 메소드로 추출할까 고민했는데.

메서드로 추출해도 각 항목에서 early return 으로 메서드를 종료하려면 조건문을 똑같이 3개써야 할것같아서.

현재는 이정도가 딱 좋다고 생각한다.

 

SOLID에 대한 정리

  • SRP - single responsibility principle 단일 책임 원칙
    • 하나의 클래스는 하나의 책임만 가진다.
    • 클래스를 생성하고 기능을 정의하면서 클래스가 가진 역할이 여러개인경우 클래스를 분리한다.
    • Money라는 클래스는 돈에대한 역할만 수행하고 돈을 저장하거나 계산하는 역할은 다른 클래스로 분리가가능하다. 돈을 저장하는건 지갑이나 , 창고같은 클래스에 맡기고 계산은 계산기가 한다.
  • OCP - Open-Closed principle
    • 확장에 열려있고 수정에는 닫혀있다.
    • 기능을 쉽게 확장하고 적게 수정
    • 인터페이스나 추상화를통해서 필수기능들을 정의하고 구현체로 기능을 구현 , 기능에대해 수정사항이나 각 상황별 다양한 구현이 필요하다면 그때에 각각 다른 구현체들로 변경하면서 기능을 확장할수있다.
  • LSP - Liskov Substitution Principle
    • 부모클래스의 인스턴스를 자식클래스의 인스턴스로 치환할수 있어야 한다.
    • 부모클래스의 기능을 확장한 자식클래스가 들어가도 동일하게 동작해야한다.
  • ISP - Interface Segregation Principle
    • 사용하지않는 인터페이스에 의존하면 안된다.
    • 만약 사용하지않는 인터페이스에 의존하게된다면 해당 인터페이스의 기능이 충분히 분리되지않았다. 더 작게 기능을 분리해서 필요한 인터페이스에만 의존하도록한다.
  • DIP - Dependency Inversion Principle
    • 상위수준모듈은 저수준모듈에 의존해서는 안된다. 둘다 추상화에 의존
    • 저수준 모듈에 의존하게된다면 저수준 모듈이 수정될때 고수준 모듈도 같이 수정해야한다.
    • 추상화에 의존하게된다면 구현부분인 저수준모듈이 수정되어도 고수준모듈은 수정되지않는다




반응형

+ Recent posts