게시판 페이지네이션 SQL

sql
1
2
3
4
5
6
const page = 1;

const query = `SELECT * FROM notice WHERE use_flag='Y' ORDER BY idx DESC LIMIT ?,?`;
const queryArgs = [10 * (page - 1), 10];

return client.query(query, queryArgs);
  • notice 테이블에서 page값에 따라 게시글 10개를 가져오는 쿼리이다
  • limit 을 이용해서 구현하였다

SELECT * FROM notice LIMIT 0,10
// 0번째부터 10개를 가져온다는 의미이다

SELECT * FROM notice LIMIT 20,10
// 20번째부터 10개를 가져온다는 의미이다

참고

inline style vs !important

!important 승

  • 갑자기 인라인에서 정의한 스타일과 !important 중에 누가 더 강력한지 궁금해 졌다
1
2
3
h1 {
color: red !important;
}
index.html
1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1 style="color: blue">hello world</h1>
</body>
</html>

Banner Maker 클론코딩

데모

기능

  • 캔버스 크기 조절
  • 실시간 캔버스 업데이트
  • 폰트 사이즈 조절
  • 폰트색 조절
  • 캔버스색에 따른 폰트색 자동조절
  • 캔버스색 조절
  • 랜덤 캠버스색
  • 이미지로 다운로드
  • 클립보드로 카피
  • 컬러 히스토리 기능
  • 컬러 히스토리 임포트, 익스포트 기능

후기

  • 타입스크립트 공부 겸 리액트도 같이 하자는 생각으로 리액트 타입스크립트를 시작했다
  • velopert님의 리액트 프로젝트에서 타입스크립트 사용하기 글을 보고 따라하다가
  • 튜토리얼을 통해 잘 만들어진 프로젝트 구조를 기반으로 만들었다
  • 리액트가 아직 익숙하지 않은데 리액트의 훅이라던지 처음부터 고급??개념들을 사용하니까
  • 사실 뭐가 좋은지, 이렇게 써서 좋은점이 무엇인지..
  • 이 개념을 도입하기전의 것들을 시도해보지 않았기 때문에 막연한 감이 있었다
  • 나는 공부할때 이론보단 무조건 실기이고, 일단 만들어보면서 익히고 이해하자라는 주의이다
  • 그래서 이것저것 쉽게만들 수 있다고 생각한다. (나중에 보면 코드가 엄청 구릴지라도..)
  • 이번에 만들면서 신경썼던 것은
  • 사용성..?? 인 것같다

  • 나는 보통 이미지를 ctrl c 카톡에다가 ctrl v 하여 톡방에 올리는데
  • 그걸위한 copy clipboard 버튼을 만들었다. 저장후 이미지 올리기는 귀찮기 때문에..

  • 또 랜덤 칼러 버튼을 만들어서 색을 쇼핑했다
js/index.jshtml-banner-maker/commit/5cccc1594309f732ea8edd8ca08c91800d97d317
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// https://stackoverflow.com/questions/3942878/how-to-decide-font-color-in-white-or-black-depending-on-background-color
/**
* Get color (black/white) depending on bgColor so it would be clearly seen.
* @param bgColor
* @returns {string}
*/
function getColorByBgColor(bgColor) {
if (!bgColor) {
return '';
}
return parseInt(bgColor.replace('#', ''), 16) > 0xffffff / 2
? '#000'
: '#fff';
}
  • 사용해보면 알겠지만 랜덤으로 생성된 배경색에 따라 폰트색도 검정색/흰색 적절한게 선택된다

  • 컬러 히스토리 기능이다
  • copy 버튼이나 download 버튼을 누르면 해당 배경색과, 폰트색이 히스토리에 저장된다
  • 만약에 같은 최근 히스토리에 저장된 색과 같은 색이면 저장하지 않도록 하였다. 버튼을 누를때마다 증식되지 않도록..
  • export 버튼을 눌러 현재 저장된 컬러 히스토리를 json 파일로 내보내는 기능도있다
  • 깃허브 페이지를 이용해 호스팅하기때문에, 내 앱은 정적이다
  • 어떻게 히스토리를 저장하고 불러올까 생각하다가 json 파일로 내보내고 다시 불러오도록 만들게 되었다

개선방향

  • 칼러픽커에서 알파값을 바꿀 수 있도록 하기
  • 폰트 변경 기능
  • textarea를 div안에 넣어버리기..? (html2canvas 사용해보기)
  • json파일로부터 컬러히스토리 임포트할때 덮어쓰기가 아니라 병합으로 불러오기
src/modules/common/copyToClipboard.js
1
2
3
4
5
6
7
8
9
10
11
export function copyToClipboard() {
const canvas = document.querySelector(`#myCanvas`);
if (!canvas) {
return;
}
canvas.toBlob(function (blob) {
// eslint-disable-next-line no-undef
const item = new ClipboardItem({ 'image/png': blob });
navigator.clipboard.write([item]);
});
}
  • 클립보드 복사 js 코드를 ts로 변환하지 못한 코드..

소스코드

참고

정처기 으악

  • 정보처리기사 책을 펴고 30분 봣는데 모니터 보는 것 보다 눈이 아파서 때려쳤다
  • 막연한 이론은 공부할 엄두가 안난다…

vscode search exclude setting

  • 검색결과에서 제외할 디렉터리, 파일 설정하기

세팅창 화면으로

  • Ctrl + Shift + P 를 누르고 setting을 입력한다
  • Preferences: Open Settings (UI)로 들어간다

search: exclude 설정

  • 검색에서 제외할 패턴들을 입력한다

검색 제외 적용하기

  • … 을 클릭해서 디테일 표시

  • file to exclude 영역에 아이콘 활성화

  • 왼쪽이 비활성화일 때
  • 오른쪽이 활성화일 때
  • 쓸때없는 것을 제외함으로써 쾌적한 검색결과를 확인할 수 있다

상위 옵션 ; files.exclude

  • vscode 탐색기에서 존재자체를 없애버리는 얘다
  • 여기서 제외된 디렉터리는 당연히 검색에서도 제외됨으로
  • search.exclude의 상위 호환이라고 볼 수 있다

gradle project 에서 runnable jar 생성하기

  • 일반 자바 프로젝트처럼 Export > Runnable JAR file로 jar을 만들게 되면 생성된 jar파일의 용량이 매우 작은 것을 확인할 수 있다
  • 내가 추가한 외부 라이브러리들이 전부 빠져서 동작하지 않는다
  • gradle project에서는 어떤 방식으로 runable jar을 만들어야 할까?

build.gradle 수정하기

build.gradle
1
2
3
4
5
6
7
8
9
10
jar {
manifest {
attributes 'Main-Class': 'tmp.gradle.project.Main'
}
from {
configurations.compile.collect {
it.isDirectory() ? it : zipTree(it)
}
}
}

  • jar { … } 에 해당하는 부분을 위처럼 새로이 추가해준다
  • attributes 'Main-Class': 'tmp.gradle.project.Main' 여기에는 자신의 메인 클래스를 지정하면 된다

jar파일 생성하기

terminal
1
2
cd {gradle project rootpath}
gradle jar
  • 자신의 그래들 프로젝트 경로에서 cmd를 켜주고
  • gradle jar 명령을 실행한다

jar파일 확인하기

  • build/libs 디렉터리에 가보면 jar파일이 생성돼있음을 확인할 수 있다
terminal
1
2
cd build\libs
java -jar {jar파일명}
  • 마지막으로 잘 실행되는지 확인한다

gradle로 외부라이브러리 설치하기

원하는 라이브러리 명 + gadle 키워드로 구글링한다

  • 나는 json-simple 을 받아보겠다

카피

  • 그래들 탭인지 잘 확인하고 카피한다
  • build.gradle 파일로와서 dependencies에 추가하고 저정한다

Gradle Project Refresh

  • 끝!

RDS 삭제시 주의사항

  • ㅠㅠ

RDS 삭제 시 자동으로 스냅샷 생성

  • RDS를 삭제하면 자동으로 스냅샷? 백업같은 것을 생성한다

  • RDS를 사용중도아니고, 삭제하니까 비용이 생긴게 삭제하면서 생긴 스냅샷 보관 비용이였다

  • RDS를 삭제한다면 RDS 삭제 후 꼭 RDS > 스냅샷 에서 생성된 스냅샷도 같이 삭제…

일렉트론으로 OBS input overlay 클론코딩

  • 오버워치 강의 영상을 보다가 영상 속 키보드 마우스 뷰어? 를 가끔 보았었다
  • 일단 깔끔한 키보드 뷰어로 흥미가 생겼었는데
  • 찾아보니까 OBS input overlay라는 OBS 전용 플러그인이었다
  • 일렉트론이라는 프레임워크를 사용하여
  • html, css, js로 데스크탑 앱으로 만들어보았다

신경 쓴 것

글로벌 키 훅?

  • 내가 만든 앱 밖에서 키가 눌리고 떼지는 걸 알아야 한다
index.js
1
2
3
4
5
const ioHook = require('iohook');

ioHook.on('keydown', (event) => {
mainWindow.webContents.send('keydown', event.rawcode);
});
  • iohook 패키지를 사용해서 해결하게 되었다
  • 그런데 iohook이 최신 버전의 일렉트론에서 실행이 안되어서 일렉트론 8.x버전으로 낮추어 진행하게 되었다

overlay window

  • 오버레이 화면에 마우스 클릭이 되지 않도록 했다
  • 오버레이 화면이 프레임을 가지지 않도록 했다
  • 오버레이 화면이 모니터 크기를 구하여 우측 하단에 생기도록 하였다 (여러 해상도 대응)

일렉트론

  • 웹 기술로 데스크탑앱을 만들 수 있게해주는 마법이다
  • 나는 요즘 js만 해왔기 때문에 일렉트론에 대해 찾아보았다
  • 일렉트론으로 만들어진 대표적인 앱으로는 비주얼 스튜디오 코드가 있다

일렉트론 시작

일렉트론 단점?

  • 엄청나게 간단한 앱일지라도 nodejs + Chromium 조합으로 용량이 상당히크다

소스코드

otp 구현하기

  • 갑자기 otp를 구현하고 싶어졌다

Unix Epoch Time (Unix Time) 구하기

js
1
new Date().getTime(); // unix time

N 구하기

js
1
2
const ts = 30; // 30초 간격으로 토큰을 생성할 것
let N = Math.floor(new Date().getTime() / (ts * 1000));
  • unix time에서 토큰 생성간격 ts 만큼을 나누고 소수점은 버려서 N을 구한다

N 16진수 변환

js
1
let N_hex = ('000000000000000' + N.toString(16)).substr(-16); // 16자리가 되도록 앞에 0 패딩추가
  • N을 16진수로 변환하는데 16자리가 되도록 처리

m 구하기 (시간 베이스 메세지 값)

js
1
let m = Buffer.from(N_hex);
  • N_hex를 byte array로 변환

K 생성하기 (K : 공유키)

js
1
2
3
4
5
const base32 = require('base32'); // base32 모듈 임포트

const random12 = `${Math.random() * Math.pow(10, 20)}`.substring(0, 12); // 12자리 랜덤숫자 생성

const K = base32.encode(random12);
  • 랜덤값으로부터 20자리의 공유키 K 구하기
  • base32 모듈 설치
  • Math.random()으로 생성한 랜덤값을 12자리 문자열로 변환하고 base32인코딩하면 20자리의 문자열을 얻을 수 있음

HMAC hash 얻기

js
1
2
3
const crypto = require('crypto');

let hmac_hash = crypto.createHmac('sha1', K).update(m).digest('hex');
  • 내장 모듈 crypto 에서 hmac hash를 얻을 수 있다

OTP 생성

js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const offset = parseInt(Number(`0x${hmac_hash[hmac_hash.length - 1]}`), 10);

// offset으로부터 4개 바이트 변환
const token_hex_4bytes = hmac_hash.substring(offset * 2, offset * 2 + 4 * 2);
let toekn_hex = '';

toekn_hex += (
'00' + (Number(`0x${token_hex_4bytes.substring(0, 2)}`) & 0x7f).toString(16)
).substr(-2);

for (let index = 2; index < token_hex_4bytes.length; index += 2) {
const element = token_hex_4bytes.substring(index, index + 2);
toekn_hex += ('00' + (Number(`0x${element}`) & 0xff).toString(16)).substr(-2);
}

const token = Number(`0x${toekn_hex}`).toString().substr(-6);
console.log(`token : ${token}`);
  • hmac_hash의 맨 마지막값을 offset으로 한다
  • hmac_hash offset부터 4바이트를 추출한고
  • offset 바이트에 & 0x7f 이진 연산 수행
  • 나머지 3개 바이트에 대해 & 0xFF 연산 수행
  • 6자리 토큰을 만든다고하면 그 결과의 뒤에서 6자리를 토큰으로 한다

개선사항

  • 생성된 토큰 값이 일부분 동일하게 유지된다

참고