MkWeb

MkWeb은 Minwhoan-Kihyeon's Webserver framework의 약자이다. 이름에서 볼 수 있듯이 최초 시작은 나와 내 친구인 hyeonic 둘이서 개발을 시작하였다.


MkWeb은 앞장에서 언급한 바 있지만, 프론트 개발자가 더 쉽고 간편하게, 내가 알지 못하는 서버에 대한 고민 없이 웹 서비스를 할 수 있게 도와주는 프레임 워크이다.

사실 https://mkweb.dev-whoan.xyz 이 페이지에 들어가면 MkWeb에 대한 설명이 조금이나마 더 문서에 맞는 어투를 사용하여 작성하였으나, 처음 공식문서를 작성하다보니 그 어색함이 있고, 표현하기에도 애매한 바가 있고, 편하게 그 내용을 정리하고 싶어서 여기에도 글을 쓴다.


MkWeb은 MVC 패턴에 맞춰 디자인된 웹서버 프레임워크이다. 그렇다고 해서 Spring Project는 아니다. Tomcat과 같은 WAS에서 동작하는 JSP 기반 웹 서버이다. (JSP라 쓰고 HTML/Javascript 라 읽는다. ㅋㅋ)그럼에도 불구하고 MVC에 맞춰 디자인한 이유는 다음과 같다.

1. 프론트 개발자가 웹서버를 쉽게 정의할 수 있어야 한다.
2. 프론트 개발자가 웹서버를 쉽게 유지/보수 할 수 있어야 한다.
3. 설계만 잘 한다면, 그에 맞게 웹서버가 동작할 수 있어야 한다.

지금이야 기본 기능이 구현된 후에서야 쓰는 글이지만, 위 3가지를 바꿔 말하면,

1. 프론트 개발자가 사용하고자 하는 View를 정의하고 (JSP), 미리 짜여진 Controller의 설계에 맞춰 기능을 잘 만든다면 웹서버가 동작하여야 한다.

2. 유지보수를 쉽게 하기 위해 프론트 개발자는 일절 Server-side Programming 지식이 없어도 되야 하므로, 프론트 개발자가 잘 사용하는 json/xml 방식을 통해 유지/보수 할 수 있어야 한다.

와 같다. 어느정도 그림이 그려 지는가?

따라서 우리 MkWeb는 MVC를 다음과 같이 정의했다.

Model: 서버의 자원 (DB, File Server 등)

View: Client가 볼 웹 페이지

Controller: 모델과 뷰 사이의 관계

Service: 컨트롤러 내에 존재하여 실제 동작하는 워커


정리해보면, MkWeb은 프론트 개발자가 json으로 정의된 컨트롤러들을 잘 정의하고, 사용하고자 하는 Service를 잘 등록해주면 웹서버가 돌아간다!

지금 지원하는 Service(Controller)는 다음과 같다.

1. RDBMS
2. RESTful API
3. MkLogger
4. FTP

개발 연혁(?)
2020.05: dev.whoan과 hyeonic의 웹서버 공부를 목적으로 MkWeb 개발 시작

2020.08: RESTful API 기능을 추가하고자 하였으나, 우리 둘의 지식 부족으로 인해 MkWeb 개발 중단

2020.09: 학기 시작과 hyeonic의 4학년 준비로 인해 MkWeb의 공동개발 중단, dev.whoan의 독자개발 진행

2021.01: koh의 관심으로 koh의 합류.

2021.03: 학기가 시작됨에 따라 MkWeb 개발 중단.


 

MkWeb

MkWeb이라는 웹서버 프레임워크를 만들기 시작했다.

누군가의 부탁이나, 어떤 대회에 참가하기 위해서가 아니라 '이러한 방법으로도 만들 수 있구나!' 하는 경험과 백엔드를 공부하기 위함이 그 목적이다.

따라서 MkWeb은 웹서버 프레임워크이며, MkWeb을 사용함으로써 편리하게 웹을 개발하자는게 그 취지이다.

특별한 점이 있다면, SSR(Server-Side Rendering) 방식이지만 어떻게 해야 프론트 개발자가 더 쉽고 간편하게, 내가 알지 못하는 서버에 대한 고민 없이 웹 서비스를 할 수 있을까 하는 방향에서 접근하기 시작했다.

위에서 언급한 '이러한 방법으로도 만들 수 있구나!'에 대한 경험은 SkyLove의 전창렬 이사님의 SkyFramework를 보고 경험했고, 나도 웹서버를 독자적으로 만들어보고 구조를 이해하고자 함이다.

처음에는 'MkWeb을 모든 대학생이 한번쯤 써보면 어떨까?' 하는 큰 꿈이 있었으나, 추가하고자 하는 기능이 많아짐에 따라 이제는 그냥 '누군가는 써봤으면' 하는 그러한 꿈도 있다.


 

웹 사이트를 제작 할 때, float: left로 둔 속성들에 대해 빈공간이 생기는 걸 볼 수 있다.

HTML을 처음 시도한다면 아래와 같은 코드를 입력했을 때, 빈공간이 없어야 한다고 생각할 수 있다.

#p-wrapper{
	float: left;
	background-color: #f1cc82;
}

.p-tag{
	display: inline-block;
	background-color: #ccc;
}
<div id="p-wrapper">
  <p class="p-tag">너</p>
  <p class="p-tag">와</p>
  <p class="p-tag">나</p>
</div>

빈공간이 보인다

그럼에도 불구하고 위 사진처럼 너 와 나 사이에 공간이 생기는걸 볼 수 있는데, 이는 버그가 아니라 HTML 문서에서 줄바꿈이 인식되어 공간이 발생한 것이다. 즉 이를 해결하기 위해서는 빈공간에 대한 사이즈를 줄여야 하는데, 다음과 같은 방법이 있다.

1. <p> 태그를 작성하면서 엔터(줄바꿈)을 하지 않는다.
2. p-wrapper내부에 있는 공간을 인식하는 것 이기 때문에, p-wrapper내의 글자 크기를 줄인다.
3. <p>태그의 닫힘 문자를 건너뛴다. (</p>를 쓰지 않는다. 이는 문제가 될 수 있어서 비추천...)

1번은 다음과 같다.

<div id="p-wrapper">
<p class="p-tag">너</p><p class="p-tag">와</p><p class="p-tag">나</p>
</div>

그러나 이 역시 p태그의 요소와 내용이 간결하기 때문에 보기 쉽지만, 클래스가 여러 개 들어가거나, 내용이 길어질 경우 꽤 tricky 해질 수 있기 때문에 비추천 한다.

그래서 내가 주로 사용하는 방법은 2번 방법이다. css에서 p-wrapper의 글자 크기(줄바꿈을 없애기 위해)를 0으로 설정하고, p-wrapper내에 직접적인 글자를 작성하지 않는 것이다. 

#p-wrapper{
	float: left;
	background-color: #f1cc82;
    font-size: 0;	/* 여기서 p-wrapper의 글자를 지우고 */
}

.p-tag{
	display: inline-block;
	background-color: #ccc;
    font-size: 16px; /* 여기서 p tag의 글자 크기를 설정한다. */
}

만약 p-tag에서 font-size를 지정해주지 않으면, 다음과 같은 문제가 발생할 수 있으니, 꼭 p-wrapper 내부의 어떤 요소에 글자를 작성한다면, 해당 요소의 css에서 글자 크기를 반드시 지정해 줘야 한다.

아무것도 없다!

정상적으로 모든 요소에 글자 크기 설정을 진행하고 나면, 아래와 같이 빈 공간이 사라진 채로 요소를 붙일 수 있다.

너와나가 드디어 붙어있다!

팀뷰어를 사용하다 보면 다음과 같은 오류로 팀뷰어를 사용하지 못할 때가 있다.

시간이 초과되었습니다.
시간 초과후 연결이 차단되었습니다. 현재 파트너로 연결하시려면 라이선스를 업그레이드 하시거나, 잠시후 다시 시도 하십시오.
이 파트너로의 연결이 10:00까지 차단됩니다. 

해당 문제를 검색한 결과, 무료 사용자에게는 연결 제한이 존재하는데 이를 초과할 경우 위와 같은 문제가 발생한다.

해당 문제는 LAN Driver의 주소를 변경하여 해결이 가능한데, 그 방법은 다음과 같다.

장치관리자 - 랜 카드 - 네트워크 주소(Locally Administered Address)

속성을 찾은 뒤, 해당 속성의 값이 없음(N) 이라면, 값을 000000000000 (12자리 수)를 입력하고 저장한 뒤에 윈도우를 다시 시작하면 된다.

값이 이미 설정되어 있다면, 해당 값을 변경하거나 없음(N)으로 설정하고 윈도우를 재시작 하면 된다.

변경 전
변경 후

만약 위와 같은 설정 후에도 팀뷰어에서 여전히 시간 초과 문제가 발생한다면, 다음과 같은 방법으로 문제를 해결할 수 있다.

1. 네트워크 설정을 원래 값으로 돌려놓는다
2. 윈도우를 재시작 한다.
3. 팀뷰어를 삭제한다.
4. 윈도우를 재시작 한다.
5. 네트워크 설정에서 값을 바꾼다.
6. 팀뷰어를 설치한다.

 

'기타' 카테고리의 다른 글

[GitHub] Link Github Respository  (0) 2021.01.16

이번에 갤럭시 버즈 프로가 생겼다. 기존에 버즈 1세대를 사용하고 있었는데, 음질 차이가 얼마나 클까 하는 생각이 있었고, 큰 기대는 하지 않았다. (애니콜 케이스만 너무 기대됐다.)

1월 30일에 삼성 갤캠스에서 주문 했는데, 2월 9일이 되어서야 배달을 받았다. (삼성 물류 노조 파업 때문에 꽤 오래 걸리는 것 같다.)

버즈 프로와 애니콜 박스

배송 받았을 때 버즈 프로보다 애니콜 케이스에 눈길이 더 갔고, 정말 너무 귀엽게 잘 만든 케이스인 것 같다.

버즈 프로 내부

그래도 버즈가 애니콜보다 더 귀한 몸이기 때문에, 버즈 프로의 박스를 먼저 열었고, 위 사진처럼 생겼다. 구성품은 버즈 프로 본체와 오른쪽 검정 박스에 USB Type C 선, 그리고 여분의 이어팁이 들어있다. 나는 귓구멍이 작기 때문에, 제일 먼저 버즈 프로의 이어팁을 S사이즈로 교체해주었다.

버즈 프로

이번 버즈 프로는 애니콜 케이스 뿐 아니라 색감도 정말 잘 뽑힌 것 같다. 원래 IT 기기는 떄 타면 안된다는 생각에 항상 검정색을 선호하는데, 이번에는 바이올렛이 예쁘게 나온 것 같아 바이올렛으로 선택했고, 후회할 일 없을 것 같다.

애니콜 케이스 장착

애니콜 케이스를 끼웠는데!! 너무 귀엽다!!! 이건 진짜 없는 사람은 따로 주문해서라도 끼워야 한다!!! 너무 귀엽잖아!!


버즈 1세대와의 비교

버즈 1세대와 비교는 "착용감", "음질", "배터리", "통화 음질", "기타 기능"으로 하겠다.

착용감

버즈 1세대 > 버즈 프로

사실 둘 다 귀에 끼우고 있을 때는 별 차이가 없다. 그런데 내가 큰 차이를 느낀 때는, 버즈를 귀에서 뺐을 때 귀의 어색함, 통증이 버즈 프로에서 꽤 크게 느껴졌다. 버즈 1세대에 적응해서 그런가? 버즈 프로를 귀에서 빼면 일단 귀가 "살것같아!"라고 하는 듯한 느낌이 든다. 이어팁을 사제로 구매해서 바꾸면 괜찮아진다는 말이 있기에, 구매 후 교체할 예정이다.

음질

버즈 1세대 << 버즈 프로

사실 별 기대하지 않은 음질이지만, 꽤 큰 차이가 느껴졌다. 버즈의 ANC를 끄고 들어도, 해상력이 꽤 좋아졌으며 버즈 1세대에서는 크게 들리지 않았던 여러 채널을 버즈 프로에선 들을수 있었다. 예를들어 드럼의 킥을 버즈에서는 '그냥 "킥"이구나'. 하고 느껴졌다면, 버즈 프로에서는 '오 "킥" 괜찮은데?' 하는 감이 느껴질 정도였으니.

배터리

버즈 1세대 > 버즈 프로 (19년부터 사용한 1세대의 수명 단축을 고려함)

프로에 더 많은 기능이 들어가서 그런가? 실제론 버즈 1세대의 사용 시간이 더 긴 것 같다. 사용했을 때는 둘의 차이를 크게 느끼지 못했지만, 수명이 줄어 든 버즈 1세대를 생각하면 프로가 더 짧은 것 같다.

통화 음질

버즈 1세대 <<~||넘사벽||~<< 버즈 프로

통화 음질은 말할 것 없다. 버즈 1세대는 통화할 때 무조건 버즈를 빼고 핸드폰으로 통화해야 했는데, 버즈 프로는 그냥 끼고 통화해도 사람들이 모른다. 심지어 여자친구는 핸드폰으로 통화하는 것 같다고 한다. 정말 정말 정말 정말 나는 이 통화 음질만으로도 정말 만족한다.

기타 기능

버즈 1세대 <<<< 버즈 프로

기타 기능은 ANC, 주변 소음 듣기, 대화 감지 등 버즈 프로에만 있는 기능들을 생각했을 때 당연히 버즈 프로의 압승이다. ANC는 정말 충격 받은게, 내 방에 컴퓨터 2대가 있는데 버즈 프로를 끼고 ANC 기능을 켜면 감쪽같이 컴퓨터의 팬 소리가 안들린다! 충격이었다. 처음에 ANC를 키고 꼈을때는 몰랐는데, ANC에서 주변 소음 듣기 모드를 처음 켰을 때 팬 소리를 듣고 헐! 대박인데? 하고 느껴졌다. 버즈 프로를 빼니, 컴퓨터의 팬 소리가 정말 크게 느껴지는게 아닌가!

주변 소음 듣기 또한 대박이다. 버즈 1세대의 주변 소음 듣기(비슷한 기능이 있는데, 이름이 기억 안난다. 똑같은가?)는 켜도 화이트 노이즈 그리고 떨어지는 기능으로 인해 별 의미 없는 기능이었는데, 버즈 프로의 그것은 정말 대박이다. 주변 사람과 자연스러운 대화가 가능하고, 그렇다 해서 버즈 프로가 재생하던 미디어가 안들리는 것도 아니다! 이것 정말 대박인데?

대화 감지 기능도 좋기는 한데, 항상 켜놓을만한 기능은 아닌 것 같다. 버즈 프로가 조금이라도 내가 말한다는 듯한 낌새를 파악하면 (작은 노래 흥얼거림이라던지, 기침이라던지, 기타 등등 마이크가 인식만 한다면) 곧바로 주변감지 기능이 켜지기 때문에, 미디어에 집중하긴 어렵기 때문이다. 그래서 나는 버즈 프로의 터치 기능을 ANC <-> 주변 소음 듣기 모드 변환으로 설정해 놓고, 대화 감지 기능은 꺼놨다.


다른걸 다 떠나서, 버즈 프로는 간만에 명작이 나온 것 같다. 

귀염뽀짝한 레트로 케이스들과 꽤 만족할만한 기능들, 그리고 재빠른 업데이트까지.

버즈 프로를 고민하고 있다면, 나는 꼭 추천하고 싶다.

이번엔 Queue와 관련된 자료구조에 대해 공부하려고 한다.

Queue는 Stack과는 반대로, FIFO(First In First Out)성질을 갖는다. 즉 먼저 들어온 녀석이 먼저 나간다.

Queue의 가장 흔한 예시로는 프린트 출력 대기열을 들 수 있는데, 먼저 출력 요청한 문서를 먼저 출력해야 하기 때문이다. 비슷한 예로 입장 대기줄 등이 있다. 

큐는 탐색에서도 스택과 함께 많이 쓰이므로 잘 알아두면 좋다. 특히 큐는 우선순위 큐, 원형 큐 ... 등 많은 종류가 있기 때문에, 기본 큐는 반드시 알아야 한다.


Queue에는 어떤게 필요할까?

Queue는 Stack과 비슷한 자료구조로, 데이터를 삽입하고 반환할 수 있어야 한다. Stack처럼 제일 위에 삽입한다고 생각해도 된다. 다만 Stack과의 차이점시작점과 끝점이 있어야 한다는 것이다. Stack은 top이라는 시작점만 존재했지만, Queue는 끝점도 가져야하는 이유는 다음과 같다.

1. 제일 먼저 들어온 녀석을 반환하기 위함
2. 큐가 가득 찼는지 확인하기 위함.

즉 Queue에도 Top만 존재했다면, 어느 녀석을 먼저 반환해야 할지, 새로 들어온 녀석은 어디로 들어와야 할지를 동시에 수행할 수 없기 때문에 끝점도 존재해야 하는 것이다.

그렇다면 rear는 어떻게 활용할 수 있을까? Queue에 데이터가 삽입되면 rear가 하나씩 증가하게 된다. 이를 조금 더 생각 해 보면, front와 rear가 같다면, Queue가 비어있는 상황이 되고, rear가 capacity와 같아지게 되면 Queue가 가득차게 되는 것이다.

따라서 Queue는 front, rear, capacity 그리고 queue를 담는 배열이 필요하다.


Queue 구현

Queue의 기본은 다음과 같다.

public class Queue {
	int front;
	int rear;
	int capacity;
	Object[] queue;
	Queue(int capacity){
		this.front = -1;
		this.rear = -1;
		this.capacity = capacity;
		queue = new Object[this.capacity];
	}
	
	public boolean isFull() {
    /*
    capacity가 10이면 배열은 0~9를 갖고, rear도 0부터 시작하기 때문에 -1을 해야한다.
    */
		return (this.rear == this.capacity-1); 
	}
	
	public boolean isEmpty() {
    /*
   	front와 rear가 같으면 비어있는 상태이기 때문에, 편의를 위해 front와 rear를 -1로 초기화 시켜준다.
    */
    	if(front == rear) {
			front = -1;
			rear = -1;
		}
		return (this.front == this.rear);
	}
}

다음으로 Queue에 데이터를 삽입하고 반환하는 함수가 필요한데, Stack에서는 이를 pop, push라고 했다면 Queue에서는 enqueue, dequeue라고 한다.

데이터를 반환하기 위해서는 front에 있는 먼저 들어온 녀석을 반환해줘야 하는데, 반환 해줄 때 마다 front의 위치를 옮겨주어야 한다.

 

왼쪽이 간편해 보인다.

위 그림을 보면, 다음 출력 문서의 위치 front는 고정되어 있고, 남아있는 문서들을 한칸씩 옮기는 오른쪽 그림과 다음 출력 문서의 위치 front를 이동시키고 남아있는 문서들은 고정되어 있는 왼쪽 그림중 어느 것이 더 편해 보이겠는가?

정답은 당연히 왼쪽 그림인 front를 옮기는게 훨씬 간단하다.

이렇게 dequeue에서 front를 옮기기 때문에, rear의 위치 또한 단순히 배열의 index를 옮겨서는 안되고, 한가지 트릭을 써야 한다. (만약 n칸짜리 queue에서 n개를 넣고 dequeue를 n번 하면, front와 rear의 크기는 같아지게 된다. 즉 queue는 비어있게 되는데, 문제는 rear의 위치가 capacity와 같아지기 때문에 동시에 가득찬 상황이 된다. 이는 Queue의 모순이지 않는가?)

rear에 사용할 트릭은, 단순히 index를 지정하는 게 아니라 Queue의 capacity로 나누어서 나온 몫이 rear의 index가 되게 해야 한다. 위 괄호안에서 든 예시로 말하자면, capacity가 10이라고 가정 했을 때, rear와 front는 모두 9가 되는 상황이다. 이 때, (rear+1) % (capacity = 10) 을 하면 삽입할 다음 index는 0이 되고, rear와 front의 차이는 다시금 10이 되기 때문에 처음부터 넣는것과 같게 된다. (+1을 해주는 이유는, 삽입할 element는 다음 번 index를 가져야 하기 때문이다.)

이를 바탕으로 enqueue를 설계하면 다음과 같다.

public class Queue {
	...
	public void enqueue(Object element) {
		if(isFull()) {
			System.out.println("Queue is Full!");
			return;
		}
		
		rear = (rear+1) % this.capacity;
		queue[rear] = element;
	}
    ...
}

dequeue 또한 enqueue와 같은 원리로 Queue의 capacity로 나눈 몫을 index로 가지면 된다. 이유는 위에서 rear에 대한 설명한 것과 같다.

public class Queue {
	...
	public Object dequeue() {
		if(isEmpty()) {
			System.out.println("Queue is empty");
			return null;
		}
		
		front = (front+1) % this.capacity;
		return queue[front];
	}
    ...
}

Queue도 Stack과 같이 element를 반환하지 않고 엿보는 peek을 가질 수 있는데, 코드는 다음과 같다.

public class Queue {
	...
	public Object peek() {
		if(isEmpty()) {
			System.out.println("Queue is empty");
			return null;
		}
		
		return queue[front+1];
	}
    ...
}

 

이제 queue의 size와 초기화를 시켜주는 함수를 만들면 된다.

size는 rear - front를 통해 구할 수 있다. 이는 내가 마지막으로 삽입한 index - 제일 먼저 삽입한 index가 되고, 5개 입력했으면 다음과 같기 때문이다 (5-0) = 5. 그런데 배열은 0부터 시작하기 때문에 front와 rear에 각각 1씩 더해주면 된다. ( (4+1) - (-1+1) ) = 5. 또한 Queue는 front와 rear의 index가 역전될 수 있으니, 절대값 으로 반환하면 된다.

초기화는 간단하게 front, rear, 모두 -1로, 그리고 queue 배열을 새로 선언하면 된다. 

코드는 다음과 같다.

public class Queue {
	...
	public int size() {
		return Math.abs( (rear+1) - (front+1) );
	}
	
	public void clear() {
		if(isEmpty()) {
			System.out.println("Queue is already empty!");
		}
		else {
			front = -1;
			rear = -1;
			queue = new Object[this.capacity];
			System.out.println("Queue has cleared!");
		}
	}
}

Queue 코드 전체 및 실행결과

 

public class Queue {
	int front;
	int rear;
	int capacity;
	Object[] queue;
	Queue(int capacity){
		this.front = -1;
		this.rear = -1;
		this.capacity = capacity;
		queue = new Object[this.capacity];
	}
	
	public boolean isFull() {
		return (this.rear == this.capacity-1);
	}
	
	public boolean isEmpty() {
		if(front == rear) {
			front = -1;
			rear = -1;
		}
		return (this.front == this.rear);
	}
	
	public void enqueue(Object element) {
		if(isFull()) {
			System.out.println("Queue is Full!");
			return;
		}
		
		rear = (rear+1) % this.capacity;
		queue[rear] = element;
	}
	
	public Object dequeue() {
		if(isEmpty()) {
			System.out.println("Queue is empty");
			return null;
		}
		
		front = (front+1) % this.capacity;
		return queue[front];
	}
	
	public Object peek() {
		if(isEmpty()) {
			System.out.println("Queue is empty");
			return null;
		}
		
		return queue[front+1];
	}
	
	public int size() {
		return Math.abs( (rear+1) - (front+1) );
	}
	
	public void clear() {
		if(isEmpty()) {
			System.out.println("Queue is already empty!");
		}
		else {
			front = -1;
			rear = -1;
			queue = new Object[this.capacity];
			System.out.println("Queue has cleared!");
		}
	}
}

아래는 실행코드이다.

public class mainQueue {
	public static void main(String[] args) {
		System.out.println("=====짧은머리 개발자=====");
		
		Queue queue = new Queue(10);
		
		queue.dequeue();
		
		for(int i = 0; i < 10; i++) {
			queue.enqueue(i);
		}
		
		System.out.println("isFull : " + queue.isFull());
		
		System.out.println("size : " + queue.size());
		
		for(int i = 0; i < 10; i++) {
			System.out.println("Dequeue : " + queue.dequeue());
			System.out.println("size : " + queue.size());
		}
		
		System.out.println("Dequeue : " + queue.dequeue());
		
		System.out.println("isEmpty : " + queue.isEmpty());
		
		queue.enqueue(555);
		System.out.println("isEmpty : " + queue.isEmpty());
		System.out.println("isFull : " + queue.isFull());
		System.out.println("peek: " + queue.peek());
	}
}

실행 결과

제네릭은 일부러 안했다. 여러분이 한번 해보세요! 쉬워요.

내가 만든 웹 서버가 HTTP가 아닌 HTTPS에서 통신하게 하려면 SSL이라는 인증서가 필요하다. Secure Socket Layer, 전송 계층 보안은 보안된 환경에서의 통신을 제공하기 위한 규약이다. 조금 왜곡을 보태 간단하게 말하면 HTTP의 취약한 해킹을 보안하기 위해 SSL을 이용함으로써 해킹으로부터 안전할 수 있는 것이다.

뿐만 아니라 간혹 웹서버 개발 중에 CORS 관련 오류나, 오류 내용에서 HTTPS에서만 가능하다. 는 문구를 확인할 수 있는데, 이를 해결하기 위해서는 HTTPS가 필요하다.

유료 HTTPS의 경우 비싼 돈이 들기 때문에 나같은 학생 개발자나 간단한 개발에서는 굳이 돈을 내고 쓸 필요가 없는데, 이 때 LetsEncrypt를 이용해 SSL 인증서를 무료로 발급받을 수 있다. (LetsEncrypt! 누르면 홈페이지로 이동)


LetsEncrypt를 우선 받아야 하는데, Github에 올라온걸 clone 해주면 된다.

git clone https://github.com/letsencrypt/letsencrypt [PATH/TO/DOWNLOAD] # 나같은 경우 /opt/letsencrypt

이후에 내려받은 폴더로 이용하여 certbot-auto를 실행시키면 된다.

certbot-auto certonly --manual --preferred-challenges=dns --email [이메일] --server https://acme-v02.api.letsencrypt.org/directory -d [SSL을 인증하고자 하는 도메인]

여러 도메인을 등록하고자 하는 경우 -d DOMAN1 -d DOMAN2 와 같이 -d를 이용해 추가해주면 된다.

이후에 certbot에서 시키는대로 하면 된다. 위 명령어가 동작하지 않는 사람들은 아래 명령을 이용하면 된다. 그러나 아래 방식의 경우 standalone이기 때문에, 위 명령어를 추천한다. 

cerbot-auto certonly --standalone -d [SSL을 인증하고자 하는 도메인]

 


사용 예시

$ ./certbot-auto certonly --manual --preferred-challenges=dns --email dev.whoan@gmail.com --server https://acme-v02.api.letsencrypt.org/directory -d *.dev-whoan.xyz

Requesting to rerun ./certbot-auto with root privileges...
Your system is not supported by certbot-auto anymore.
certbot-auto and its Certbot installation will no longer receive updates.
You will not receive any bug fixes including those fixing server compatibility or security problems.
Please visit https://certbot.eff.org/ to check for other alternatives. Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator manual, Installer None
Obtaining a new certificate Performing the following challenges:
dns-01 challenge for dev-whoan.xyz
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NOTE: The IP of this machine will be publicly logged as having requested this certificate. If you're running certbot in manual mode on a machine that is not your server, please ensure you're okay with that.
Are you OK with your IP being logged?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

(Y)es/(N)o: Y

위에서 *.dev-whoan.xyz에 대한 와일드카드 SSL 인증서 발급을 요청했고, 이후에 설명이 나온다. IP 로그에 대해 거절(N)하면, SSL 발급이 취소된다. Y를 입력하면 다음과 같이 DNS TXT 레코드를 입력하라고 하는데(도메인 주인 확인을 위한), 사용중인 도메인의 TXT 레코드를 추가해주면 된다.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please deploy a DNS TXT record under the name
_acme-challenge.dev-whoan.xyz with the following value:
입력해야할 TXT 값
Before continuing, verify the record is deployed.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue

이 때, TXT 레코드를 추가해 줄 때 _acme-challenge만 TXT name으로 입력해주면 된다. 뒤의 dev-whoan.xyz까지 모두 입력하면 안된다. (모두 입력하면 txt는 _acme-challenge.dev-whoan.xyz.dev-whoan.xyz에 기록되기 때문)

이후에 다음과 같은 글이 뜨면 성공!

Waiting for verification...
Cleaning up challenges
Subscribe to the EFF mailing list (email: dev.whoan@gmail.com).
IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/dev-whoan.xyz/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/dev-whoan.xyz/privkey.pem
Your cert will expire on 2021-05-04. To obtain a new or tweaked version of this certificate in the future, simply run certbot-auto again. To non-interactively renew *all* of your certificates, run "certbot-auto renew"
- If you like Certbot, please consider supporting our work by:
Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
Donating to EFF: https://eff.org/donate-le

이 외의 글이 뜨면 오류도 함께 출력되니, 그에 맞게 해결하면 된다. (대게 와일드 카드 혹은 TXT 레코드를 이용한 도메인 주소 검증에서 실패하는 경우, _acme-challenge를 잘못 설정해서 그런 경우다.)

하루에 10번 이상인가? 실패하면 24시간 이후에 인증서 발급이 가능하니 주의하도록 하자.

* 참고로 와일드 카드의 경우 자동 갱신이 불가능하기 때문에, 주의하길 바란다.

와디즈를 자주 보는 편인데, Type C 레드빈 마그네틱 코어7 최대 100w 충전 선이 있길래 펀딩했다. 친구초대 이벤트로 15000P가 있었는데, 이를 사용하여 거의 공짜에 가까운 가격으로 펀딩했다.

스마트 기기가 늘어감에 따라 동시에 충전할 수 없을까 하는 생각이 있었고, 때마침 최대 120W Type C *2, 65W Type A * 2 짜리 충전기를 펀딩 해놓은 상태라, 그에 걸맞는 속도를 내는 USB 선도 필요하여 펀딩했다.

Type C 플랫형으로 스마트폰용 하나, 태블릿용 하나 총 2개를 구매했고, 드디어 펀딩한 제품을 배송받았다.

충전 선과 마그네틱 코어7 커넥터


알고보니 제품이 잘못 배송되어 다시 받았다.

아직 따로 펀딩한 타사의 충전 어댑터는 배송을 받지 못해서 제 속도를 테스트해보진 못하지만, (속도는 노트북에 연결하여 얼마나 빠른 시간에 충전되는지를 통해 확인할 예정이다). 일단 갖다 대기만 하면 착 붙는 마그네틱 속성 덕분에 꽤 편리하게 단말기 충전을 하고 있다.

갖다 대면 붙는다

사실 갖고 있는 플립은 최대 15W라서 (...) 별 의미 없지만, 위 영상처럼 그냥 근처에 두면 붙기 때문에 편의성에 의의를 두기로 했다. (...)

최대 45W 보조배터리를 통해 탭7을 충전해보니까 꽤 빠르게 충전된다. (그래봤자 이전 25W에 비해 10분정도밖에 안빨라졌지만.. 이건 삼성 충전 속도 때문이니..)

여튼 선이 착 붙는 편의성과 편리성은 정말 최고다!

+ Recent posts