728x90

업무 회고

3개월 좀 넘게 이제 지금의 회사를 다니는 중인데,

사실 정말 짧은기간이라고 봐도 무방하다.

근데 이 3개월동안 회사에서 했던건 정말 많았다.

왜냐면 내가 지금 혼자 백엔드를 맡아서 개발하고 있으니까 말이다.

전에 있던 백엔드분은 보지도 못했고 따로 인수인계 받은게 없었으니

기존의 기능에서 추가적으로 개발하는데 꽤나 애를 먹었었다.

인수인계 과정

그러니까 지금 백엔드 프로젝트 1개가 인계한 순서가

전 백엔드 근무자 → 프리랜서 → 나

이 순서로 인계가 되었기 때문에 뭐 제대로 받은게 없었다.

당연히 받은 코드도 이해하는데 꽤나 걸렸으며,

특히 테스트 코드 가 없었다....

어떤식으로 돌아가는지에 대한 이해를 전혀 할 수가 없어서 짜증이 났지만,

그래도 이게 또 기회랍시고 나는 넥스트스텝의 TDD 과정을 들은 것을 토대로 테스트를 작성했다.

테스트 코드

테스트 코드는 정말 작은것부터 시작을 했다.

누구한테 물어볼 그런것도 없었거니와, 추가적으로 알아야 하는것은 구글신 에게 의존하는 수 밖에 없었고, 그러면서 동시에 어떤 라이브러리에 대한 공식문서 보는것이 좀 당연해졌다.

지금 구조는 회사의 백엔드 기술력이 오로지 나 혼자가 일궈낸 것이 되는것이다.

처음 왔을 때부터 그랬으니까 뭔가 도입하거나 하려는 것도 내가 배워서 적용만 하면

되는것인데,

방향을 잡아줄 수 있는 사람이 없다는 것 이게 정말 아쉬운 부분이었다.

하지만 단위테스트를 적용해놓고나서 보니까 중복된 코드도 많이 보였고,

쓸모없는 로직, 불필요한 주석

추가적으로 어떤 변수에 대해 사용하지 않은데 향상된 for문, 람다 forEach가 섞여있는 모습들이 많았다.

무조건 분기해서 만든 긴 ifElse 조건문들...

와서 모두 디자인패턴에 대한 공부, TDD Clean Code with Java 12기 에 학습하면서

다 안좋게 보여서 전부 고쳤다.

테스트가 항상 통과되고 안전하다는 보증을 서줬기 때문에 믿고 막 고칠 수 있었던것 같다.

협업

협업에서도 아쉽지만 우리 회사에는 Git에 대해 좀 생소할 수도 있고, Jira, Slack 등등... 사용하지를 않아서 협업 질문에 대해서 다 협업은 하고싶다고들 얘기를 하셨다.

그렇기 때문에 Git Flow에 대해서 회의실에서 내가 발표를 하고 이 Flow대로 Git을 제대로 사용해보자 도입해보자 건의를 했다.

대답들은 전부 긍정적이셔서 이렇게 협업의 장을 내가 열었다(?) 고 봐도 될 것이다.

Jira에 대해서는 들어는 보셨지만, 사용해보지는 않았다고 하셔서 이전 직장에서의 사용했던 경험과, 그리고 지금 진행중인 스터디 에서 사용했던 거로 설명을 전부 했었다.

그리고 제일 중요했던 것...

스웨거 도입

여태 API문서 에 대한건 전부 엑셀파일로 백엔드 개발자 분이 직접 만들거나 또는 Postman에 올리거나 하는식으로 진행을 했다고 한다....

당시에 나는 이 방식이 정말 귀찮았다.

이거를 내가 API 개발함과 동시에 문서화를 하는게 낫다고 판단했다.

왜냐면 서버가 돌아가면서 그안에서 테스트를 해볼 수 있기 때문이었다.

그리고 구축을 해두었으니 별도로 따로 타이핑을 해서 문서화 해야하는 불편함을 좀 덜 수 있었기 때문이다.

그래서 Spring Rest DocsSwagger 2 중에 고민을 했지만,

디자인이 좀 알록달록하고 Spring Rest Docs보다 Swagger가 설정이 좀 더 편했으니

이게 좀 더 합리적이라고 생각했다..

프론트엔드 한분이 스웨거는 많이 보셨다고 한게 한번의 작용을 했다고도 볼 수 있다. ㅋㅋㅋㅋㅋㅋㅋ

여기까지 해서 GitSwagger, Jira까지 협업에 대한 밑바닥부터 조금 만들었다.

마이그레이션 경험

일단 Mybatis 기술을 사용한 것이 정말 많았다...

이 기술 쓰는 것 그 자체에 대한 고찰이 생겼었는데

그 이유는 바로 명색이 백엔드 그것도 자바 개발자인데,

Mybatis 안의 그 핵심 쿼리들, 그리고 그 안에 비즈니스 로직을 다 넣게 되는 것이다.

그래서 원하는 데이터만 딱딱 뽑아오게끔 거의 설계가 됐었다.

전직장도 동일하다(물론 iBatis였지만)

자바를 많이 보고 객체지향적인 고민을 하는것이 아니라,

데이터적인 생각, 항상 데이터만 이렇게 가져오면 된다에 심혈을 기울여서

쿼리에서 데이터만 받아서 처리해주면 된다. 라는 생각만 갖게 됐던 것 같다.

그래서 이를 탈피하고자 이전에 Django 팀 프로젝트를 했을때 사용했던 ORM을 생각해서

공부하려고 JPA스터디를 참여하였다.

좋은 사람들을 만났고 많이 배웠으며 질문도 환영하는 느낌으로 받아들 주셨기에

길다면 길고 짧다면 짧았지만 얻어간게 정말 많았다.

지금도 질문을 하면 다들 들어주시더라

그렇게 해서 윗부분에 설명했던 테스트코드 와 같이 MybatisJPA

성공적으로 바꿨다.

쉘 스크립트 작성기

일단 지금 사용하는 스프링부트

이 프레임워크에는 내장톰캣이 구성되어있다.

근데도 과제를 수행하는 프로젝트에서 그 회사쪽의 요청으로 외장톰캣을 사용했다.

내장 톰캣이 있다고 말씀을 드렸지만 외장톰캣을 사용해야 한다고 그러시더라.

거기에 깃랩을 사용하여 소스관리를 하지만? 소스관리일뿐 그것을 가져와서 자동화는 따로 시키지 않더라.

이부분이 궁금했지만 그래도 외주를 받아서 우리가 과제를 진행했기 때문에 그쪽이 갑이어서 건의는 더이상 하지 않았던 기억이 있다.

일단 뭐 배포를 해야되는데 내가 없을 때는

프론트 개발자 한분이 전부 배포를 했다고 한다.

근데 배포하는 방식이

계속 그냥 콘솔에 cd xxx, sudo xxx, ...등등 일일이 타이핑을 치고계셔서 좀 놀랐다.

이 과정이 귀찮지는 않은가 여쭤보았고 여쭤보기도 전에 머릿속으로 당연히 번거롭고 귀찮다는걸

나도 느끼면서 질문했는데 당사자도 귀찮았다고 생각한다.

그래서 내가 할 수 있는 선에서의 최선이 바로 이 쉘스크립트 작성이었다.

이거로 인해서 반복되는 명령어 그리고 그것들을 하나하나 입력하는데에 대한 시간들에 대해

많이 줄였다. 정확하게 시간을 재지는 않았지만 빨라도 20초? 걸리던거 그냥

~: ./deploy.sh

이거만 시켜주면 되니까 이 명령어 치는데 얼마나 걸릴까???

생각은 읽는 사람의 몫이다.

뭐 이렇게 나름의 노력을 했더니 개발이 그냥 재미있다.

뭔가를 계속 찾아서 불편한 부분을 개선하는 나의 일련의 노력들 그리고 그걸 해냈을 때 희열감 그리고 그것에 대한 기억은 대단하다.

개월이 지날 수록 이런 회고글 좀 더 쓰는식으로 진행을 하겠다.

728x90

'Diary' 카테고리의 다른 글

블로그를 옮기고 최신 근황  (0) 2022.08.13
업무 리팩토링에 대한 회고  (0) 2022.08.10
1개월 1일 1커밋 회고  (0) 2022.08.07
TDD Clean Code with Java 12기 - 1주차  (0) 2022.08.05
728x90

첫 일기

코드는 깃허브에 있다.

CS bool 정리 와도 연관이 되지만, 좀 자유롭게 쓰고 싶어서

이렇게 일기 카테고리로 작성한다.

들어가면서

우선은 나와 같이 노력해주시는 창훈님과 밑바닥 컴퓨팅 시스템을 같이

오프라인 스터디로 진행하고 있다.

처음엔 둘다 멋대로 또는 자기가 머릿속으로만 생각했던 대로 nand2tetris를 구현했었는데, 진행하다 보니까

뭔가 제대로 잡히질 않았다..

태초마을로..

그래서 논리게이트 하나씩 알면서 그리고 진리표를 보았을 때, 어떤 게이트들을 써서 구현을 해야하는지..

그런 개념을 확립하고 머리로만 짜는게 아니라 삽질도 해가면서 논리식으로 표현하는게 목표였다.

그래서 2장을 진행하다가 다시 1장으로 돌아오게 되었다. ㅋㅋㅋㅋㅋ 근데 결과적으로 얻어간 지식이 더 많은것 같다.

이게 대학교 때 물론 내가 기계쪽이 전공이지만, 디지털 논리 회로라는 과목에서 했던것들이다.

주의깊게 보지 않았었을 뿐더러, 교수님이 강의해주실 때는 되게 재미가 없었고, 왜 배워야하는지 어디에 쓸 수 있는건지

그렇게 생각하며 무난하게 보냈던 과목인거같다.

새롭게 지금 CS지식을 학습하며 이 책에서 구현해야하는 조건들을 하나하나 카르노맵도 그려가면서,

또는 집합을 그려가면서 구현해보니까 이 진리표일땐, 이렇게 구현이 되는구나! 를 많이 느꼈던 것 같다.

일단 불대수 법칙에는 아래 이미지와 같은 법칙들이 명시되어 있다.

이건 수학을 배울때에도 봤던 기억이 있어서

그렇게 어렵지 않았다.

스크린샷 2021-10-24 오후 11 12 10

고생했던 자료의 흔적


논리식 부딪히기

KakaoTalk_Photo_2021-10-24-22-40-16

이부분은 처음에 논리식을 파헤치려 할 때 이해 안되는 부분 그리고 왜 그렇게 나오는지에 대해 설명하기 위해

밴-다이어그램을 그려가면서 설명하면서 의논을 했던 부분이다.

논리식 도출해보기

KakaoTalk_Photo_2021-10-24-22-40-00

어느정도 이해를 한 상태에서 진리표를 보고 집합으로 값을 도출하는 과정이다...

ㅋㅋㅋ 이렇게 정리하면서 보니까 안까먹겠다 싶더라 역시 쓰면서 대화도 해보고 팍팍 부딪히면서 학습하는게

기억에도 잘남고 동시에 보게되면 바로 아! 하고 깨닫게 되는 순간이 훨씬 많다는 사실..

정말 많이도 그렸고 단순한 And, Or 인데도 불구하고 애를 먹었다.

논리식을 도출할 때 a와 b에 해당하는 진리값을 곱해서 나온게 output인데 And, Or에 따라서
곱으로 도출하는것, 합으로 도출하는것 그 논리 합, 곱 이게 섞이면서 식을 몇번을 갈아 엎었다.

카르노맵을 생각해냈다!!!

KakaoTalk_Photo_2021-10-24-22-40-08

이제 머리로만 간소화해서 짜다가는 필요에 의해서 카르노맵 이 있다는걸 생각해냈고,

검색을 통해 2변수, 3변수, 4변수 등등의 카르노맵을 그리는 법,

그러면서 짝수개를 묶어서 값을 도출해낼 수 있다는 지식까지 습득한 채로 다시 논리식을 도출해보게 되었다.

결국 모양으로 쳐둔 것들이 최종 값인데 곱연산은 And, 합연산은 Or 이런식으로 '기호는 Not으로 해서
게이트를 연결해보았다.

그러니까 이해도 되면서 어떻게 동작하는지도 알겠고 너무 재밌더라 👍

이때부턴 탄력받아서 아래것들도 쭉쭉 진행했었다 🔥🔥🔥

2변수 카르노맵 간단 풀이

Y
X
0 1
0 0 1
1 1 1

Or의 진리표이다. 이 진리표가있을 때 짝수개는 인접한것 끼리 묶을 수 있다고 했다.

  • X가 1일때, Y값에 관계없는 경우 (Y, Y' 둘다 상관없다)
  • Y가 1일때, X값에 관계없는 경우 (X, X' 둘다 상관없다)

두가지로 도출해 볼 수 있다.

두가지는 + 로 연결이 된다.

그래서 Out = X + Y 가 되는 것이고 그래서 논리합 이라고 부르는 것이다❗️

이게 트이니까 정말 재밌어서 얼른 하나라도 더 빨리 풀어보려고 했던 것 같다.

KakaoTalk_Photo_2021-10-24-22-39-53

KakaoTalk_Photo_2021-10-24-22-39-43

KakaoTalk_Photo_2021-10-24-22-39-31

정리

아무튼 이런식으로 진행해서 재밌고 잘 이해되게 1장을 클리어한 것 같다.

다음 다이어리에선 반가산기, 전가산기 쪽을 다뤄보도록 하는 시간을 가져보겠다. 👏

뭔가 이렇게 풀이해내는 것이 읽는것도 읽는 맛이 나고 뭘 어떻게 했는지 보이게 되는것

그게 직관적이라 상당히 괜찮은것 같기도 하다. 물론 이미지 찍어서 업로드 하는것이 번거롭지만

그래도 또 어제의 나보단 한 층 더 성장했으니까 그거로 만족한다 😇

타인과 비교하지 않고 혼자 성장치를 잘 쌓다보면 언젠가는 주변에 좋은 개발자들과 어깨를 나란히 할 수 있는 날

그날이 올거라고 반드시 믿는다.

728x90

'CS' 카테고리의 다른 글

불 연산 회고  (0) 2022.08.09
불 논리 정리 2  (0) 2022.08.09
HTTP  (0) 2022.08.07
불 논리 정리  (0) 2022.08.07
728x90

여기서 나오는 모든 예제는 깃허브에 있다.

📌 변수

변수는 값을 저장하는 공간이다.

📌 변수란?


프로그래밍에서의 변수란, 값을 저장할 수 있는 메모리상의 공간이라고 한다.

이 공간에 저장된 값은 변할 수 있기 때문에, 변수라고 이름이 붙여졌다.

변수란, 단 하나의 값을 저장할 수 있는 메모리 공간

그렇기 때문에, 새로운 값을 저장하면 기존의 값은 날아가게 된다.

📌 변수의 선언과 초기화


📌 변수의 선언

변수의 선언 방법은

public class Class {
    int age; // age라는 이름의 변수 선언
    // int : 변수 타입
    // age : 변수 이름
}

변수 타입은 변수에 저장될 값이 어떤 타입인지를 지정해주는 것이다.

변수 이름 변수의 이름이다. 이 변수 이름 은 메모리 공간에 이름을 붙여주는 것이다.

int age; 메모리 주소지가 1이라고 가정했을 때,

int 타입의 age 변수를 1 주소지에 할당해라 가 되는 것이다.


📌 변수의 초기화

변수를 선언한 이후에는 변수를 사용할 수 있는데, 그전에 반드시 변수를 초기화 시켜주어야 한다.

메모리는 여러 프로그램이 공유하기 때문에, 다른 프로그램에 의해 그 메모리가 알 수 없는 값이 들어있을 수 있기 때문에

반드시 초기화를 해주어야 한다.

여기서 연산자가 나오는데 바로 대입 연산자(=) 이다.

이 대입연산자로 변수에게 값을 할당해줄 수 있다.

int age = 26 → 변수 age를 선언하고 26으로 초기화 시켰다.

같은 타입이라면

int a;
int b;
int x = 0;
int y = 0;

//같은 코드
int a, b;
int x = 0, y = 0;

하지만 코딩 컨벤션은 한줄에 하나를 할당하는게 대부분의 규칙처럼 정형화 되어있다.

이 부분에 대해선 협업하는 사람들에 따라 갈리겠지만 보통의 개발자들은 전자를 선호할 것이다.

변수의 초기화란, 변수를 사용하기 전에 처음으로 값을 저장하는 것

@Test
void test() {
    int year = 0;
    int age = 21;

    year = age + 2000; //변수 age + 2000
    age += 1; //age 저장된 값 1증가

    assertThat(year).isEqualTo(2021);
    assertThat(age).isEqualTo(22);
}

대입 연산자는 바로 수행되는 것이 아니라, 우변에 모든 계산이 끝난 후에 할당을 진행한다.

메서드 안에서 변수를 초기화하는 경우엔 명시적으로 int a = 0; 이렇게 초기화를 진행해주지만,

클래스 변수 즉, 전역변수는 int a; 이렇게 선언만 해주더라도 int 타입에 String을 넣는다거나

혹은 String 타입에 int를 넣는

참사를 방지하기 위해 컴파일러에서 아래에 출력한 0, null처럼 자동으로 값을 할당해 준다.


📌 두 변수의 값 교환하기

너무 유명한 swap 예제이다. CallByValue, CallByReference

조금 원초적으로 책에서는 설명되어있는데,

int a = 10;
int b = 20;

두 값을 서로 바꾸려고 할 때, b에 저장된 값을 a에 해버리면 둘다 20이 될 것이다.

그러기에 중간 변수를 하나 두어 스왑을 해주어야 한다.

int a = 10;
int b = 20;
int temp = 0;

temp = a;
a = b;
b = temp;

이렇게 해서 값을 바꿔주어야 한다.


📌 변수의 타입

값은 크게 문자, 숫자 두가지 부류로 나눌 수 있다.

숫자는 또 실수와 정수로 나뉜다.

이런 값의 종류에 따라서 값이 지정될 공간의 크기, 저장형식을 정의한 것이 자료형이다.

이 자료형에는 문자형(char),정수형(byte, short, int, long), 실수형(float, double) 등이 있다.

저장하려는 값의 특성에 따라 알맞은 자료형을 맞춰주면 된다.

📌 기본형과 참조형

  • 기본형
    • 변수는 값을 실제 값을 저장
    • boolean, char, byte, short, int, long, float, double
  • 참조형
    • 값이 저장되어 있는 주소를 값으로 가짐
    • 8개의 기본형을 제외한 나머지 타입

변수 타입에 대한 정리는 여기

📌 상수와 리터럴

상수(constant)는 말 그대로 변하지 않는 값. 그래서 변수 앞에 final을 붙여주면 된다.

final은 선언과 동시에 무조건 초기화를 해주어야하며, 그 이후엔 변경할 수 없다.

static 키워드도 같이 붙여주게 되는데,

붙이는 이유는 어떠한 객체에 final 변수가 하나 있다면 그 객체를 생성할 때마다,

final변수는 생기게 된다. 하지만 static 을 사용해서 메모리에 적재하게 두면 1개의 변수만이 여러개의 클래스에서 공유하게 되는 것이다.

이 상수를 쓰는 이유는 나는 2가지라고 생각한다.

첫째, 메서드안에 연산을 수행할 때, 하드 코딩 으로 숫자를 집어넣게 되면 무슨일을 하는 숫자인지 모를 수 있다.

그렇기에 상수로 떼어내어서 어떤 역할을 하는 변수인지 의미있게 이름을 준다.

둘째, 반복되는 숫자를 상수로 도출하면 반복코드를 제거할 수 있고, 들어가 있던 값을 상수만 바꿔주면

수정작업도 용이하기 때문이라고 본다.

📌 형변환

캐스팅(casting) 이라고도 부르며 서로 다른 타입간의 연산을 수행할 때,

같은 타입으로 변환시켜주는 것이 바로 형변환이다.

변수나 리터럴 앞에 변환하고자 하는 타입을 명시해주면 변환이 된다.

(타입)피연산자

기본형에서 boolean을 제외한 나머지 타입들은 서로 형변환이 가능하다.

하지만 기본형과 참조형간의 형 변환은 불가능하다.

그리고 기본형 타입에서도 작은 값을 큰값으로 그러니까 int형 변수를 long 자료형에 넣는다고 한다면

자동 형변환이 일어나게 된다.


2021-11-06 추가 업데이트

자바 공식문서 페이지를 보면서 추가적으로 여러개를 더 정리해보았다.

📌 변수의 종류

📌 인스턴스 변수 (비정적 필드)

static 키워드 없이 선언된 필드에 저장한다.

해당 값이 클래스의 각 인스턴스(각 개체마다)에 고유하기 때문에 인스턴스 변수라고 한다.

📌 클래스 변수(static 필드)

이 변수는 클래스가 인스턴스화 된 횟수에 상관없이 정확히 한개만 있음을 컴파일러에게 알려준다.

static은 초기화 후에 변경되지 않게 하려고 final을 같이 붙여서 사용해준다.

📌 지역 변수

객체가 필드에 상태를 저장하는 방식과 유사하게, 메서드는 임시 상태를 지역변수에 저장한다.

지역변수는 선언된 메서드에서만 볼 수 있다. 나머지 클래스에서는 액세스 불가능

📌 매개변수

흔히 보던 main메서드

public static void main(String[] args){} 에서 args가 바로 매개 변수이다.

매개변수는 항상 필드 가 아닌 변수 로 분류된다는 것이다.

728x90

'Java' 카테고리의 다른 글

변성  (0) 2022.08.11
일급 컬렉션  (0) 2022.08.09
상태 패턴 적용  (0) 2022.08.07
프로젝트 리팩토링  (0) 2022.08.07
728x90

쿼리 속도 개선

업무중 엄청 느린 쿼리들이 여럿 있었다.

그냥 단순히 select * from 으로 모든 칼럼을 조회하는 것이 아니라

조건에 맞는 데이터를 뽑는 로직들이 있었는데

일단 복합키로 PK가 잡혀있는 상황이고, 세개의 복합키들 중에 두개는 중복되는게 많았다.

다시 말해 카디널리티가 낮은 것 들이었다.

JPA를 사용해서 더럽게 뽑아온 다음에 가공하려고 했었지만, 데이터가 많아서 그것조차 조금 어려운 상황이었다.

계속 똑같은 쿼리문을 반복적으로 실행시키니까 처음 사용자입장에선 빨리 받아오는 것처럼 느낄지 모르겠다 (근데 이것도 느리다.)

하지만 맨 뒤쪽에 와서 이 쿼리를 실행하게 된 사람은 앞 사람들이 요청한 그 트랜잭션들이

처리된 후에나 실행이 되기 때문에 쿼리가 1초이고 누른 사람이 10명이라면

마지막 사람은 10초보다 그 이상이 걸려서 데이터를 받아볼 수 있게 된다.

이게 얼마나 흉측한 상황인지 10명만 해도 이정도로 지연된다는 것 자체가 웃기다.

개선 방법

우선은 DB 실행계획을 보았다.

explain select ...

이 구문으로 실행계획을 찾아봤다.

나는 인덱스를 생성하는 방식으로 쿼리 개선을 진행하였다.

블로그 상에 있던 규칙을 가져왔다.

쿼리 최적화를 위한 7가지 체크리스트 에서 내용을 좀 추려봤다.

요약해보자면

SELECT시에는 꼭 필요한 칼럼만 불러와야 한다.

(결국 조건에 맞는 데이터를 줄 단위로 가져온 후 거기서 추출하고 싶은 각 칼럼을 도출해 내는것이니까 메모리적으로는 이득일 수 있다.)

  1. 조건을 부여할 시에는 가급적이면 DB에 별도의 연산을 끼워넣지 않는다.
  2. LIKE 사용 시, 와일드카드 문자열(%)String 앞부분에 배치하지 않는 것이 좋다.
  3. SELECT DISTINCT, UNION DISTINCT 같은 중복 값을 제거하는 연산은 최대한 사용하지 않아야 한다.
  4. 쿼리 실행 순서는 WHERE절이 HAVING절보다 먼저 실행된다. WHERE절로 데이터를 작게 가공해둔다면 효율적인 연산이 가능하다.
  5. 3개 이상의 테이블을 JOIN할 경우에 크기가 가장 큰 테이블을 FROM절에 배치하고 JOIN절에는 남은 테이블을 작은 순서대로 배치하는 것이 좋다.
  6. 자주 사용하는 데이터의 형식에 대해서는 미리 전처리된 테이블을 따로 보관 or 관리하는 것도 좋다.

일단 이중에 해당하는 사항은 1, 2번을 사용한 쿼리인데
1번은 바람직하게 잘 실행했다.

하지만 2번에서 WHERE조건이 조금 복잡하게 구성이 되어있었다.

애시당초에 조회속도가 그냥 느려서 조건을 검색하여도 느렸다.

그러니까 복합키 중에 두 키가 같은 값으로 쭉 정제가 되어있었기 때문에

카디널리티가 높은 (그러니까 중복 값이 적은) 칼럼을 인덱스를 잡아주어 스캔을 하도록 유도하려고 했다.

실행 계획

image

실행 계획에는 위와 같이 구성이 되어있다.

실행 계획에 대한 설명이 필요하다면 여기에서 확인 가능하다.

해결

그래서 나는 카디널리티가 높은 칼럼을 찾아야 했는데 복합키중 1개면서 동시에 유니크한 그런 데이터 칼럼이 바로 날짜였기 때문에 이 칼럼에 인덱스를 부여해주었다.

CREATE INDEX 해당칼럼별명
on 테이블 (인덱스칼럼);

부여해준 후

그렇게 해서 쿼리 테스트를 진행했다.

결과는 아래와 같다.

개선 전

느린쿼리

개선 후

개선쿼리

시간을 정말 많이 줄이게 되었다.

728x90

'DB' 카테고리의 다른 글

[DB] 옵티마이저  (0) 2022.08.05
728x90

도커 컴포즈

업무하면서 도커를 적용해야하는 프로젝트가 있다. 아직 풀리지 않은 이슈들도 있는데

그거는 다른 포스팅에서 진행하도록 하고,

우선 도커 컴포즈에 대해 포스팅을 진행한다.

짧은 회고

지금까지 나의 방식은

Dockerfile을 생성해주고, 그 후에

도커 명령어를 통해서 개별 이미지들을 run을 해주어서 빌드를 해줬었다.

근데 이걸 쉘스크립트로 짜도 정말 불편했다.

내가 Vue와 Springboot를 둘다 배포해주어야 하는 상황이었기 때문이다.

Vue 그러니까 npm관련 실행 명령어는 또 Nodejs 공식문서를 봐가면서 습득을 했고

Vue + SpringBoot를 둘다 배포하는데에 성공했다.

일단 흐뭇했음.. 😂

따로 빌드하면서 설정해줄 것은 없어서

VueJs Dockerfile

Dockerize Vue 에서 읽은 내용으로 빌드를 진행

FROM node:lts-alpine

RUN npm install -g http-server

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY . .

RUN npm install -g @vue/cli

RUN npm run build

EXPOSE 8080

CMD [ "http-server", "dist"]

Spring Boot Dockerfile

FROM openjdk:11-jdk

ARG JAR_FILE=build/libs/\*.jar

COPY ${JAR_FILE} app.jar

ENTRYPOINT ["java", "-Dspring.profiles.active=prod", "-jar", "/app.jar"]

그리고 docker-compose는 최 상위 루트에서 yml파일로 만들어주어 실행하도록 한다.

  • docker-compose
    • Vue Dockerfile
    • SpringBoot Dockerfile

이런식으로 진행되어 컴포즈 파일이 두가지를 다 빌드관리를 해주는 것이다.

docker-compose.yml

version: "3.8" # 버전 명시 도커 버전이 19.03.0+ 이면 3.8

services:
  vue:
    volumes:
      - 소스위치 :/app # 소스위치 : vue WORKDIR 과 매칭
    build:
      context: 배포하고자 하는 경로 # 빌드하는 위치
      dockerfile: 도커파일 경로 # 도커파일 명시
    ports:
      - "8080:8080" # 포트 포워딩

  spring:
    build:
      context: 배포하고자 하는 경로
      dockerfile: 도커 파일 경로
    ports:
      - "8081:8081" # 포트 포워딩

이렇게 배포에 성공하였다.

요즘 자바 API설계보다 서버쪽 관리에 업무가 더 치중되어있는것 같다.

도움이 아주 많이 되는것 같다.

네트워크 지식도 점차 쌓여가는 것이 흡족하다.

728x90

'클라우드' 카테고리의 다른 글

OpenSSH  (0) 2022.08.09
[Docker] 도커란  (0) 2022.08.05
728x90

HTTP란?

HTTP(Hyper Text Transfer Protocol) 텍스트 기반 통신 규약으로써,

인터넷에서 데이터를 주고받을 수 있는 프로토콜입니다.

HTTP는 신뢰성 있는 데이터 전송 프로토콜을 사용하는데

TCP 프로토콜에서는 데이터 송수신을 위해 클라이언트와 서버의 소켓이 연결되어 있어야 하며, 데이터가 유실되면 데이터

재전송을 요청함으로써 신뢰성을 보장합니다.

즉, 신뢰성 있는 데이터 전송이 가능하다는 장점으로 인해 HTTP, FTP, TELNET 등 대부분의 응용 계층 프로토콜의

전송 계층으로 사용 됩니다.

이렇기 때문에 인터넷의 결함이나 약점에 대한 걱정 없이 고유의 기능을 구현하는데에만 집중이 가능합니다.

웹 클라이언트와 서버

웹 콘텐츠들은 웹 서버에 존재합니다. 웹 서버는 HTTP로 통신을 한다고 했었기 때문에, HTTP서버 라고도 합니다.

웹 서버는 인터넷의 데이터를 저장하고, 클라이언트가 요청한 데이터를 제공해줍니다.

쉽게 설명하면 프론트단에서 Request(요청) 값을 보내면 서버쪽에서 Response(응답) 객체를 내려주는 원리라고

생각하면 되겠습니다.

이것이 바로 World Wide Web(통칭 WWW) 의 기본 요소입니다.

리소스

웹 서버는 웹 리소스들을 관리하고 제공합니다.

원초적인 이 웹 리소스는 웹 서버 파일 시스템의 정적 파일입니다.

이 정적 파일이라 함은, HTML파일부터 시작하여, 이미지파일 등등 모든 종류의 파일이 여기에 포함됩니다.

이런 정적파일이 주가 되기는 하지만, 무조건적으로 정적 파일일 필요는 없습니다.

이 동적 리소스인 경우에는 사용자마다, 시간, 정보 등등에 따라 각기 다른 콘텐츠들을 제공합니다.

미디어 타입

미디어 타입은 서버에서 메소드인자에 많이 들어간것을 생각해보면 됩니다.

대표적인 예가 바로

setContentType(MediaType.APPLICATION_JSON) 입니다.

인터넷은 수천가지 데이터 타입들을 다루기 때문에, HTTP에서는 웹에서 전송되는 객체 각각에 MIME이라는

데이터 포맷 타입을 붙이게 됩니다. 이 MIME은 전자메일 시스템에서 주로 사용을 했었는데,

각 문서와 함께 올바른 MIME 타입을 전송하도록, 서버가 정확히 설정하는 것이 중요합니다.

브라우저들은 리소스를 내려받았을 때 해야 할 기본 동작이 무엇인지를 결정하기 위해 MIME 타입을 사용합니다.

문법

MIME 타입의 구조는 주타입 / 부타입 으로 이루어져 있습니다.

이러한 예시들이 있습니다.

  • application/json
  • text/html
  • image/jpeg

URI (Uniform Resource Identifier)

웹 리소스는 각기 고유한 이름을 가지고 있기 때문에, 클라이언트는 해당 리소스를 지목하여 호출할 수가 있습니다.

글에대한 리소스 URI라면 이렇게 표현이 가능합니다.

https://lsj8367.github.io/spring/Spring-Filter-Interceptor/

  1. https 프로토콜을 사용해서
  2. lsj8367.github.io 로 이동한 후
  3. spring/Spring-Filter-Interceptor 에 해당하는 리소스를 가져와줘!

하면 이제 해당하는 글에 대한 포스팅을 보여주게 될 것입니다.

728x90

'CS' 카테고리의 다른 글

불 논리 정리 2  (0) 2022.08.09
불 논리 회고  (0) 2022.08.07
불 논리 정리  (0) 2022.08.07
재귀 알고리즘  (0) 2022.08.06
728x90

스테이트 패턴 적용기

모든 코드는 깃허브에 있음을 알린다.

현재 패스트캠퍼스의 디자인 패턴 강의를 수강도 하고있고, 동시에 디자인 패턴에 대한 책도 한권 읽었었다.

그러니까 메인 메소드만 있는곳에서 디자인패턴을 적용하여 연습을 조금 했었다.

그리고 프로젝트에 도입을 했었는데 약간 2% 부족한 패턴이 탄생했었다.

지금에서의 완벽한 디자인패턴

프로젝트를 통해서 유저 포인트를 조건에 따라 변경해주는 로직이 있었다.

스크린샷 2021-10-06 오후 11 19 38

자... 예전의 나는 보잘 것 없었다.

ㅋㅋㅋㅋ 이게 그때 당시의 나에겐 최선이었을지도??

아무튼 각설하고 저 로직을 이제 바꿔보려고 한다.

해당로직의 bean이라는 객체는 단지 저 포인트와 해당 유저의 아이디만 가지고 있는 일종의 DTO이고,

네이밍마저 가독성을 해쳤다.

이제부터 리팩토링 과정을 적어보려고 한다.

리팩토링 과정

스크린샷 2021-10-06 오후 11 31 24

어설프지만 클래스 다이어그램을 그려보았다.

일단 PointState가 최종적으로 저 4개의 랭크들을 주입받아 사용하기 때문에 화살표를 그어주지는 않았다.

예전 방식으로는 저 클래스 하나하나마다 빈을 등록해주고 switch를 배제한대신 if문에 return방식으로 구현했다. 물론 뒷단으로 로직을 빼긴했지만 뒤지다보면 어찌됐든 나오게 되어있는 방식이었다...

일단 어떻게 구현했는지 하나하나 보도록하자

일단 기본 틀이되는 Rank부터 살펴보자

Rank.java

public interface Rank {
    int getRankEnum();
    int giveUserPoint();
}

랭크를 확인할 수 있는 로직과, point를 세팅해주는 로직이 있다.

RankEnum.java

@Getter
@NoArgsConstructor
@AllArgsConstructor
public enum RankEnum {
    FIRST(1),
    SECOND(2),
    THIRD(3),
    ANOTHER(-1);

    private int rank;
}

랭크별 enum을 관리하기로 생각해서 따로 빼주었다.

public class First implements Rank {

    @Override
    public int getRankEnum() {
        return RankEnum.FIRST.getRank();
    }

    @Override
    public int giveUserPoint() {
        return 3000;
    }

}

나머지 클래스들은 안에 반환값만 다르기 때문에 First만 가져오겠다.

PointState.java

public class PointState {
    private final Map<Integer, Rank> pointStateMap = new HashMap<>();

    public PointState(List<Rank> ranks) {
        ranks.forEach(rank -> {
            pointStateMap.put(rank.getRankEnum(), rank);
        });
    }

    public Rank getPointStateMap(int rank) {
        return pointStateMap.get(rank);
    }

}

구현체인 PointState이다.

@Configuration
public class RankConfig {

    @Bean
    public PointState pointState() {
        return new PointState(Arrays.asList(new First(), new Second(), new Third(), new Another()));
    }

}

나는 이렇게 Config 클래스에서 런타임때에 초기화를 해주는데 생성자의 형식에 맞게
Rank에 해당하는 4가지 구현체를 넣어주었다.
이제 int형의 랭크 등급에 따라 추가할 포인트값을 받을 수 있게 되었다.

이제는 이게 정상적으로 동작하는지 확인을 해보기 위해 테스트코드를 먼저 짜도록 해보겠다.

테스트코드 작성

PointState.java

@SpringBootTest(classes = RankConfig.class)
class PointStateTest {

    @Autowired
    PointState pointState;

    @Test
    void test() {
        Rank rank = pointState.getPointStateMap(RankEnum.FIRST.getRank());

        assertThat(rank.getRankEnum()).isEqualTo(RankEnum.FIRST.getRank());
        assertThat(rank.giveUserPoint()).isEqualTo(3000);
    }

}

Rank설정을 해준 RankConfig를 가져온 후에 적용시켰다.

그렇지 않으면 마이바티스가 섞여있기 때문에 지금 에러가 발생한다 😱

스크린샷 2021-10-06 오후 11 44 16

정상적으로 통과하는것을 볼 수 있다.

이제 switch 로직을 리팩토링 할 생각이다.

스크린샷 2021-10-06 오후 11 45 29

이렇게 바꾸었고 이제 조건에 분기하여 포인트 값을 할당하지 않고 바로 작업할 수 있게 되었다!!!

정리

스크린샷 2021-10-06 오후 11 19 38

이랬던 로직이

스크린샷 2021-10-06 오후 11 45 29

이렇게 바뀐거 보고 디자인패턴 정말 좋다고 느낀다.

하나하나 배우면서 계속 적용해보고 싶어지는게 당연하달까..

아무튼 좋은 경험이었고 프로젝트 리팩토링이 잘되어가는것 같다 👍

728x90

'Java' 카테고리의 다른 글

일급 컬렉션  (0) 2022.08.09
변수  (0) 2022.08.07
프로젝트 리팩토링  (0) 2022.08.07
디자인 패턴 - Bridge Pattern  (0) 2022.08.07
728x90

템플릿 이슈

제목과 그리고 최상단의 주제인 템플릿 이슈 사항에 대해서 정리한다.

먼저 업무에서 생겼던 이슈사항부터 소개한다.

회사에서 postgreSQL을 사용한다.

그게 중요한 것은 아닌데, 각자의 db마다 방언이 있기 나름이고

테스트에서는 h2DB로 테스트를 하면서 방언도 h2방언을 설정해주었다.

로직중에 특정 date가 현재 시간보다 작은 경우를 쿼리로 찾아야했었는데,

to_date() 함수를 어떻게 적을것인가에 대한 문제였다.

왜 이 문제가 발생했냐면,

일단 기본적으로 여러개의 조건중에 저 하나가 들어가있었고, 문자열로 된 시간형식인 yyyyMMddHHmm 형식을 가지고 비교를

했어야 했기 때문이다.

이슈 파헤치기

일단 queryDsl에서는 서브쿼리나 랜덤 등등 어떤 특정 조건을 만족해야 하는 식들같은 경우엔

하나같이 끝에 Expressions가 붙었다.

대표적인건 뭐 JPAExpressions이다.

image

위 사진은 JPAExpressions의 일부이다.

이런식으로 다 함수들이 들어있을줄 알고 date 함수도 저런곳에 있겠지 하고 실행을 시키려고 했는데

존재하지가 않았다.

검색해본 결과 또 Template 류가 나오게 되었는데

DateTemplate 라는 곳에서 date관련 기능들을 사용할 수

있다고 해서 찾아보게 되었다.

image

DateTemplate 를 만드려면

ExpressionsdateTemplate() 메소드를 사용해서 만들 수 있었다.

image

메소드의 인자들이다.

그래서 나도 똑같이 넣어주기 위해서

DateTemplate date = Expressions.dateTemplate(
    LocalDateTime.class, "template???",
            genesisReservation.reservationEndDt,
            Expressions.constant("yyyyMMddHHmm")
)

이렇게 코드를 작성해주었다.

근데 작성하고 보니까 저기값은 String의 값인데

어떻게 넣어줘야하는지 감이 잡히질 않았다.

그래서 뒤지게된 JpaQueryFactory

image

JpaQueryFactory 의 내부중 일부를 가져왔다.

JPAQueryFactory는 생성자에 별도 템플릿을 주지 않으면

JPAProvider를 통한 JpQLTemplate를 가져온다.

여기보면 비슷하게 생긴 JPQLTemplates가 있다.

저길 타고 들어가보니 또 Templates를 상속받고 있었다.

JPQLTemplate에는 이제 익숙한 부분 쿼리문들이 메소드로 쭉 이어져 있었는데

그중의 일부를 보면

image

이렇게 생긴것들이 쭉 있다.

그래서 함수를 가져다가 저 조건에 맞게 쓰면 되는구나!

하고 유레카를 외쳤다. 굉장히 오래걸렸었다...😅

다시 돌아와서 템플릿 자리에 이렇게 바꿔주었다.

DateTemplate date = Expressions.dateTemplate(
    LocalDateTime.class, "to_date({0}, {1})",
            genesisReservation.reservationEndDt,
            Expressions.constant("yyyyMMddHHmm")
)

따옴표를 써서 형식을 넣어줄거다 라고하면

to_date({0}, {1})

이렇게 해주면 잘되지만,

to_date({0}, '{1}')

이러면 형식을 맞춰주지 않으면 에러가 발생한다.

image

to_date({0}, '{1s}')

이럴경우엔 또 잘 실행이 되는데

string을 넣을 경우에 s를 붙이고 따옴표로 완성시키면 되는것인가 하면서 아직도 궁금한데 이부분은 해결하지 못했다.

마무리

이제 해결했으니까 본론으로 돌아와 querydsl을 다시 작성한다면,

@RequiredArgsConstructor
public class Test {
    private final JPAQueryFactory JpaQueryFactory;
    public void test() {
        DateTemplate toDate = Expressions.dateTemplate(
            LocalDateTime.class, "to_date({0}, {1})",
            genesisReservation.reservationEndDt,
            Expressions.constant("yyyyMMddHHmm")
        );

        return jpaQueryFactory.selectFrom(table)
                    .join(joinTable)
                        .on("조인조건")
                            .where("조건".and(toDate.lt(LocalDateTime.now())))
                     .fetch();
    }
}

제목 없음

이런식으로 잘 들어오게 된다.

값도 테스트에서 비교하면 가정법 검증이 잘된다.

사이드 프로젝트에서의 이슈 사항

비슷하지만 다른 이슈였다.

사이드 프로젝트에서는 현재 MariaDB를 사용한다.

여기서는 다른 db와 다르게 rand() 를 사용한다.

postgreSQL, h2DB는 random()을 사용하는데

이것도 마찬가지로 템플릿에 대한 이슈였다.

JPAQueryFactory에서 조금 다른걸 설명하고 싶은데

일단 조회쿼리는 JPAQuery<>를,

수정쿼리는 JPAUpdateClause, 삽입 JPAInsertClause,
삭제 JPADeleteClause를 사용한다.

그래서 나는 조회쿼리를 사용해야 했기 때문에 JPAQuery 클래스
안쪽을 찾아보았다.

image

자 이렇게 보면 아까 위에서 설명했던 JPQLTemplates가 기본값으로

설정되어있기 때문에 그 템플릿을 사용하여 실행해서

그 끝쪽에 있는 Templates에는

image

이미지 처럼 들어있었다.

그러니까 NumberExpressions의 random이 먹통이었던 것이다.

아무리 방언을 따른다고 해도 템플릿에 저장되어 있는것은

그대로 가는것 같다.

생각한 결론은,

그러면 bean으로 등록하면 되지 않을까?

였다.

그래서 바로 QuerydslConfiguration 설정을 진행해주었다.

일단 우리는 Hibernate를 사용해서

기본값은 HQLTemplates 였고, 그래서

상속받아서 구현해주었다.

import com.querydsl.core.types.Ops;
import com.querydsl.jpa.HQLTemplates;

public class MariaDBTemplates extends HQLTemplates {
    public static final MariaDBTemplates DEFAULT = new MariaDBTemplates();

    protected MariaDBTemplates() {
        this(DEFAULT_ESCAPE);
        add(Ops.MathOps.RANDOM, "rand()");
        add(Ops.MathOps.RANDOM2, "rand({0})");
    }

    public MariaDBTemplates(char escape) {
        super(escape);
    }
}

이렇게 구현해주고 나니 이제 빈을 등록해주면 되었다.

@Configuration
public class QuerydslConfig {
    @PersistenceContext
    private EntityManager entityManager;

    @Bean
    public JPAQueryFactory jpaQueryFactory() {
        return new JPAQueryFactory(entityManager);
    }

    @Bean
    public JPAQuery<NewBook> jpaQuery() {
        return new JPAQuery<>(entityManager, MariaDBTemplates.DEFAULT);
    }
}

이렇게 새책 테이블에 관한 random값을 사용할 수 있을것 같았고,

바로 querydsl을 짜러 갔다.

생성자 주입으로 먼저
private final JPAQuery<NewBook> jpaQuery;

를 받아준 뒤에 로직은 그대로 두면 되겠거니 해서

그대로 두었다.

정상적으로 실행된다!!!!

컴퓨터를 옮기고 작업에 추가를 해줘야겠다.

위대로 설정하면 안된다. 상황 설명해주겠다.

현재 프로젝트는 위 설정처럼 진행했다.

그럴때 나오는 로그가 신박하다.

정리했을 처음처럼 random은 한번은 잘 출력되는데 문제는 그 이후부터이다.

스크린샷 2021-09-28 오후 11 54 47

이런식으로 새로고침을 할 때마다 random이 계속 호출된다.

그러니까 이 where절과 order by만 N + 1이 되는 것이다.

이게 일회성인지는 모르겠지만, 싱글톤 빈 주입으로 진행을 해주게 되면

계속 저걸로 영구적으로 바뀌어서 그런지 계속 추가가 된다.

처음은 몇줄 안되서 이렇겠지만 심각한 문제를 발생시킬것이다.

그 해결책은, 일단 빈주입이 아니라 쓰는 부분에만 특별하게 new를 해줘서 커스텀 해주는것이다.

@Override
public List<NewBook> selectRandom(int limit) {
    JPAQuery<NewBook> jpaQuery = new JPAQuery<>(entityManager, MariaDBTemplates.DEFAULT);
    return jpaQuery.from(newBook)
            .orderBy(NumberExpression.random().asc())
                .limit(limit)
            .fetch();
}

이런식으로 사용처에만 할당해주니까 정상적으로 rand()를 한번만 출력해주게 되었다.

결론

이제 조금씩 문서도 찾고, 라이브러리를 직접 뒤져가면서 학습해보니 결국 원초적인 지식이 가장 중요했다.

더 좋은 방법이 있다면 댓글로 남겨주셔도 좋습니다. 🙏

이렇게 보면 또 마이바티스처럼 되는 경향이 있다.

좀 더 데이터를 더럽고 간단하게 정제하고 자바쪽에서 부하를 걸어주는 방식을 계속 생각해야한다.

아무튼 오늘도 성장했다.

728x90
728x90

모든 코드는 여기

불 논리

NAND 게이트

Nand 게이트는 유니버셜 로직 게이트라고도 부르는데

이 게이트 하나만으로 모든 바이너리 동작을 제어할 수가 있다.

Nand게이트는 다음과 같은 불 함수를 계산한다.

a b Nand(a, b)
0 1 0
0 1 1
1 0 1
1 1 0

기본 논리 게이트

Not

단일 입력 Not게이트 Converter라고 불리며 input값의

반대값으로 Output을 내준다.

in out
0 1
1 0

And

And는 곱연산으로서, 입력 값이 둘다 1일 경우에만 1을 출력해준다.

a b And(a, b)
0 0 0
0 1 0
1 0 0
1 1 1

Or

Or는 합연산으로, 입력값 2개중 1개가 1이라면 1을 출력해준다.

a b Or(a, b)
0 0 0
0 1 1
1 0 1
1 1 1

Xor

배타적 논리합 이라고 불리며, 두 입력값이 서로 다를 경우 1

그렇지 않으면 0을 출력한다.

a b Xor(a, b)
0 0 0
0 1 1
1 0 1
1 1 0

멀티플렉서(Multiplexer)

멀티플렉서는 3입력 게이트로 selector 비트를 이용하여

나머지 두개 데이터 비트 입력중 하나를 선택.

a b sel Mux(a, b)
0 0 0 0
0 1 0 0
1 0 0 1
1 1 0 1
0 0 1 0
0 1 1 1
1 0 1 0
1 1 1 1

image

디멀티플렉서

이름만봐도 멀티플렉서의 정반대 기능을 수행한다고 알 수 있다.

디멀티플렉서는 선택한 비트에 따라 출력선 중 하나를 선택해 입력 신호를 보낸다.

sel a b
0 in 0
1 0 in

image (1)

멀티비트

멀티비트는 버스라는 멀티배열에 대한 연산을 수행하게 되어있다.

이 연산들은 쭉 같은 논리연산을 나열하여 비트의 쌍마다 연산을 수행해주는 것이다.

여기서는 16비트에 대한 연산을 진행했다.

AND 연산으로 예를 들어보겠다.

원래 하나의 비트 XY에 대하여 결과값을 AND(X,Y) 했던것을 16개를 동시에 진행한다고

생각하면 된다.

아래의 모든 예시는 전부 16비트 연산으로 이루어져있다.

AND16

X0 ~ X15 Y0 ~ Y15 AND16(X,Y)
0000000000000000 0000000000000000 0000000000000000
0000000000000000 1111111111111111 0000000000000000
1111111111111111 0000000000000000 0000000000000000
1111111111111111 1111111111111111 1111111111111111

기본 게이트의 연산과 같은데 갯수만 늘어난 것이므로 설명은 생략하도록 하겠다.

OR16

X0 ~ X15 Y0 ~ Y15 OR16(X,Y)
0000000000000000 0000000000000000 0000000000000000
0000000000000000 1111111111111111 1111111111111111
1111111111111111 0000000000000000 1111111111111111
1111111111111111 1111111111111111 1111111111111111

NOT16

IN NOT16(X,Y)
0000000000000000 1111111111111111
1111111111111111 0000000000000000

MUX16 멀티플렉서

X Y SEL MUX16(X,Y,SEL)
0000000000000000 0000000000000000 0 0000000000000000
0000000000000000 0000000000000000 1 0000000000000000
0000000000000000 1111111111111111 0 0000000000000000
0000000000000000 1111111111111111 1 1111111111111111
1111111111111111 0000000000000000 0 1111111111111111
1111111111111111 0000000000000000 1 0000000000000000
1111111111111111 1111111111111111 0 1111111111111111
1111111111111111 1111111111111111 1 1111111111111111

다입력

입력이 여러개인 다입력 게이트이다.

다입력에는 다입력/멀티비트 도 같이 묶어서 정리하겠다.

Or8Way

image

Or8Way의 논리게이트 인터페이스는 다음과 같다.

Mux4Way

image

이것의 멀티비트라고 한다면

이 같은 그림이 16개가 같이 수행된다고 생각하면 된다.

셀렉터가 2개이기 때문에

A B C D SEL[2] MUX16(X,Y,SEL)
0000000000000000 0000000000000000 0000000000000000 0000000000000000 00 0000000000000000
0000000000000000 0000000000000000 0000000000000000 0000000000000000 01 0000000000000000
0000000000000000 0000000000000000 0000000000000000 0000000000000000 10 0000000000000000
0000000000000000 0000000000000000 0000000000000000 0000000000000000 11 0000000000000000
0001001000110100 1001100001110110 1010101010101010 0101010101010101 00 0001001000110100
0001001000110100 1001100001110110 1010101010101010 0101010101010101 01 1001100001110110
0001001000110100 1001100001110110 1010101010101010 0101010101010101 10 1010101010101010
0001001000110100 1001100001110110 1010101010101010 0101010101010101 11 0101010101010101

sel 0번째로 a,b 그리고 c,d 를 연산 한후에
나온 결과값 o1, o2를 sel 1번째로 연산하여 결과값을 출력해주는 구조이다.

Mux8Way

image

4Way의 구조를 2개로 만들어서 결과값을 도출해주는 8입력 멀티플렉서이다.

a b c d e f g h sel out
0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 000 0000000000000000
0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 001 0000000000000000
0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 010 0000000000000000
0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 011 0000000000000000
0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 100 0000000000000000
0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 101 0000000000000000
0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 110 0000000000000000
0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 0000000000000000 111 0000000000000000
0001001000110100 0010001101000101 0011010001010110 0100010101100111 0101011001111000 0110011110001001 0111100010011010 1000100110101011 000 0001001000110100
0001001000110100 0010001101000101 0011010001010110 0100010101100111 0101011001111000 0110011110001001 0111100010011010 1000100110101011 001 0010001101000101
0001001000110100 0010001101000101 0011010001010110 0100010101100111 0101011001111000 0110011110001001 0111100010011010 1000100110101011 010 0011010001010110
0001001000110100 0010001101000101 0011010001010110 0100010101100111 0101011001111000 0110011110001001 0111100010011010 1000100110101011 011 0100010101100111
0001001000110100 0010001101000101 0011010001010110 0100010101100111 0101011001111000 0110011110001001 0111100010011010 1000100110101011 100 0101011001111000
0001001000110100 0010001101000101 0011010001010110 0100010101100111 0101011001111000 0110011110001001 0111100010011010 1000100110101011 101 0110011110001001
0001001000110100 0010001101000101 0011010001010110 0100010101100111 0101011001111000 0110011110001001 0111100010011010 1000100110101011 110 0111100010011010
0001001000110100 0010001101000101 0011010001010110 0100010101100111 0101011001111000 0110011110001001 0111100010011010 1000100110101011 111 1000100110101011

DMux4Way

반대인 디 멀티플렉서이다.

여기부터는 실습에서도 멀티비트는 하지 않았기에 하나만 쓰도록 하겠다.

이건 입력을 1개를 받아서 4개의 결과값을 도출해주는 정반대 플렉서이다.

image

in sel a b c d
0 00 0 0 0 0
0 01 0 0 0 0
0 10 0 0 0 0
0 11 0 0 0 0
1 00 1 0 0 0
1 01 0 1 0 0
1 10 0 0 1 0
1 11 0 0 0 1

DMux8Way

image

이것도 Mux8Way와 반대의 특성을 가지고 있으며 4Way 2개가 붙어진 것이다.

in sel a b c d e f g h
0 000 0 0 0 0 0 0 0 0
0 001 0 0 0 0 0 0 0 0
0 010 0 0 0 0 0 0 0 0
0 011 0 0 0 0 0 0 0 0
0 100 0 0 0 0 0 0 0 0
0 101 0 0 0 0 0 0 0 0
0 110 0 0 0 0 0 0 0 0
0 111 0 0 0 0 0 0 0 0
1 000 1 0 0 0 0 0 0 0
1 001 0 1 0 0 0 0 0 0
1 010 0 0 1 0 0 0 0 0
1 011 0 0 0 1 0 0 0 0
1 100 0 0 0 0 1 0 0 0
1 101 0 0 0 0 0 1 0 0
1 110 0 0 0 0 0 0 1 0
1 111 0 0 0 0 0 0 0 1

정리

이렇게 1장 불 논리가 끝이났다.

처음 구현을 시작했을 때에는 NAND 게이트로 쭉쭉 구현을 진행했는데

특징이라고 한다면 컴파일 자체가 가령 Or를 구현해야 한다고 치면

Or만 써서 바로 구현할 수가 없게 해놨다.

이런 함수를 하나씩 쓰려면 그 전의 동작을 알게끔 한것인가?

하는 생각이 들었다.

그리고 마지막으로는 결국 구현이 다 되어서 BUILTIN이라는 기능으로

tools - builtInChips 안의 자바 클래스파일들 이 있으면

해당 기능을 사용할 수 있었다.

마지막으로는 그것으로 구현해두고 나머지는 주석처리를 진행했다.

이진수를 다루면서 완전 밑바닥을 체험하니 재밌기도 하고 어렵기도 했다.

728x90

'CS' 카테고리의 다른 글

불 논리 회고  (0) 2022.08.07
HTTP  (0) 2022.08.07
재귀 알고리즘  (0) 2022.08.06
[알고리즘] 그리디  (0) 2022.08.04

+ Recent posts