그럼에도 불구하고 위 사진처럼 너 와 나 사이에 공간이 생기는걸 볼 수 있는데, 이는 버그가 아니라 HTML 문서에서 줄바꿈이 인식되어 공간이 발생한 것이다. 즉 이를 해결하기 위해서는 빈공간에 대한 사이즈를 줄여야 하는데, 다음과 같은 방법이 있다.
1. <p> 태그를 작성하면서 엔터(줄바꿈)을 하지 않는다. 2. p-wrapper내부에 있는 공간을 인식하는 것 이기 때문에, p-wrapper내의 글자 크기를 줄인다. 3. <p>태그의 닫힘 문자를 건너뛴다. (</p>를 쓰지 않는다. 이는 문제가 될 수 있어서 비추천...)
그러나 이 역시 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에서 글자 크기를반드시지정해 줘야 한다.
정상적으로 모든 요소에 글자 크기 설정을 진행하고 나면, 아래와 같이 빈 공간이 사라진 채로 요소를 붙일 수 있다.
이번에 갤럭시 버즈 프로가 생겼다. 기존에 버즈 1세대를 사용하고 있었는데, 음질 차이가 얼마나 클까 하는 생각이 있었고, 큰 기대는 하지 않았다. (애니콜 케이스만 너무 기대됐다.)
1월 30일에 삼성 갤캠스에서 주문 했는데, 2월 9일이 되어서야 배달을 받았다. (삼성 물류 노조 파업 때문에 꽤 오래 걸리는 것 같다.)
배송 받았을 때 버즈 프로보다 애니콜 케이스에 눈길이 더 갔고, 정말 너무 귀엽게 잘 만든 케이스인 것 같다.
그래도 버즈가 애니콜보다 더 귀한 몸이기 때문에, 버즈 프로의 박스를 먼저 열었고, 위 사진처럼 생겼다. 구성품은 버즈 프로 본체와 오른쪽 검정 박스에 USB Type C 선, 그리고 여분의 이어팁이 들어있다. 나는 귓구멍이 작기 때문에, 제일 먼저 버즈 프로의 이어팁을 S사이즈로 교체해주었다.
이번 버즈 프로는 애니콜 케이스 뿐 아니라 색감도 정말 잘 뽑힌 것 같다. 원래 IT 기기는 떄 타면 안된다는 생각에 항상 검정색을 선호하는데, 이번에는 바이올렛이 예쁘게 나온 것 같아 바이올렛으로 선택했고, 후회할 일 없을 것 같다.
애니콜 케이스를 끼웠는데!! 너무 귀엽다!!! 이건 진짜 없는 사람은 따로 주문해서라도 끼워야 한다!!! 너무 귀엽잖아!!
사실 둘 다 귀에 끼우고 있을 때는 별 차이가 없다. 그런데 내가 큰 차이를 느낀 때는, 버즈를 귀에서 뺐을 때 귀의 어색함, 통증이 버즈 프로에서 꽤 크게 느껴졌다. 버즈 1세대에 적응해서 그런가? 버즈 프로를 귀에서 빼면 일단 귀가 "살것같아!"라고 하는 듯한 느낌이 든다. 이어팁을 사제로 구매해서 바꾸면 괜찮아진다는 말이 있기에, 구매 후 교체할 예정이다.
음질
버즈 1세대 << 버즈 프로
사실 별 기대하지 않은 음질이지만, 꽤 큰 차이가 느껴졌다. 버즈의 ANC를 끄고 들어도, 해상력이 꽤 좋아졌으며 버즈 1세대에서는 크게 들리지 않았던 여러 채널을 버즈 프로에선 들을수 있었다. 예를들어 드럼의 킥을 버즈에서는 '그냥 "킥"이구나'. 하고 느껴졌다면, 버즈 프로에서는 '오 "킥" 괜찮은데?' 하는 감이 느껴질 정도였으니.
배터리
버즈 1세대 > 버즈 프로 (19년부터 사용한 1세대의 수명 단축을 고려함)
프로에 더 많은 기능이 들어가서 그런가? 실제론 버즈 1세대의 사용 시간이 더 긴 것 같다. 사용했을 때는 둘의 차이를 크게 느끼지 못했지만, 수명이 줄어 든 버즈 1세대를 생각하면 프로가 더 짧은 것 같다.
통화 음질
버즈 1세대 <<~||넘사벽||~<< 버즈 프로
통화 음질은 말할 것 없다. 버즈 1세대는 통화할 때 무조건 버즈를 빼고 핸드폰으로 통화해야 했는데, 버즈 프로는 그냥 끼고 통화해도 사람들이 모른다. 심지어 여자친구는 핸드폰으로 통화하는 것 같다고 한다. 정말 정말 정말 정말 나는 이 통화 음질만으로도 정말 만족한다.
기타 기능
버즈 1세대 <<<< 버즈 프로
기타 기능은 ANC, 주변 소음 듣기, 대화 감지 등 버즈 프로에만 있는 기능들을 생각했을 때 당연히 버즈 프로의 압승이다. ANC는 정말 충격 받은게, 내 방에 컴퓨터 2대가 있는데 버즈 프로를 끼고 ANC 기능을 켜면 감쪽같이 컴퓨터의 팬 소리가 안들린다! 충격이었다. 처음에 ANC를 키고 꼈을때는 몰랐는데, ANC에서 주변 소음 듣기 모드를 처음 켰을 때 팬 소리를 듣고 헐! 대박인데? 하고 느껴졌다. 버즈 프로를 빼니, 컴퓨터의 팬 소리가 정말 크게 느껴지는게 아닌가!
주변 소음 듣기 또한 대박이다. 버즈 1세대의 주변 소음 듣기(비슷한 기능이 있는데, 이름이 기억 안난다. 똑같은가?)는 켜도 화이트 노이즈 그리고 떨어지는 기능으로 인해 별 의미 없는 기능이었는데, 버즈 프로의 그것은 정말 대박이다. 주변 사람과 자연스러운 대화가 가능하고, 그렇다 해서 버즈 프로가 재생하던 미디어가 안들리는 것도 아니다! 이것 정말 대박인데?
대화 감지 기능도 좋기는 한데, 항상 켜놓을만한 기능은 아닌 것 같다. 버즈 프로가 조금이라도 내가 말한다는 듯한 낌새를 파악하면 (작은 노래 흥얼거림이라던지, 기침이라던지, 기타 등등 마이크가 인식만 한다면) 곧바로 주변감지 기능이 켜지기 때문에, 미디어에 집중하긴 어렵기 때문이다. 그래서 나는 버즈 프로의 터치 기능을 ANC <-> 주변 소음 듣기 모드 변환으로 설정해 놓고, 대화 감지 기능은 꺼놨다.
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!");
}
}
}
내가 만든 웹 서버가 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
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시간 이후에 인증서 발급이 가능하니 주의하도록 하자.
오래전부터 태블릿에서 사용할 무선 마우스가 있으면 좋겠다는 생각을 했다. 그래서 태블릿에 사용할 무선 마우스를 찾고 있었는데, 내게 필요한 조건은 다음과 같았다.
1. 태블릿과 블루투스 페어링을 통해 연결이 되어야 할 것 2. 태블릿 이외의 다른 기기에도 연결이 가능해야 할 것 3. 오랜시간 사용해도 손에 무리가 적을 것 4. 적당한 가성비가 있어야 할 것
1번과 2번을 충족하는 제품은 많이 있었지만, 이와 동시에 3, 4번을 충족하는 제품은 없었다.
대표적으로 로지텍의 Anywhere, M720, 그리고 페블을 눈에 두고 있었지만 Anywhere은 너무 비쌌고, 로지텍 페블은 시기만 맞으면 1만원 중후반대에 제품을 구매할 수 있었지만 생김새 때문에 3번을 충족하지 못했다.
그래서 Anywhere와 페블의 중간에 형성중인 가격대, 3번을 충족하는 로지텍의 M720을 구매할 까 많이 생각했지만 4만원이라는 돈을 주고 사기에는 너무 아까웠다.
마우스 사는것을 위와 같은 이유로 미루고 미루다 어느날 페이스북의 광고에 내가 마우스 검색한 것을 인지했는지, 샤오미의 무선 마우스 광고가 나오는게 아닌가?
샤오미 하면 가성비, 그리고 이미지로 봤을 때 딱 느껴지는 오래 사용해도 손목에 무리가 적을 것 같은 생김새!
그래서 바로 구매를 했다. 12900원의 제품 가격과 배송비 2500원을 합해 15400원에 구매했으니, 페블보다도 저렴한 것이 아닌가!!
배송받은 제품은 다음과 같이 생겼다.
제품을 구매할 때는 무소음 마우스인지 몰랐는데, 박스 전면에 Silent Edition이라 적혀있는 걸 보고 무소음 마우스라는걸 알았다.
후면에는 각종 설명이 중국어로 적혀있기 때문에, 눈길을 주지도 않았다.
개봉 하면 다음과 같이 생겼는데, 구성품은 정말 마우스 본체 하나, 제품 취급 설명서, 그리고 제품 사용 설명서 이렇게 3개가 들어있다. 건전지는 동봉되어 있지 않으니 따로 AAA 건전지 두 개를 준비해야 한다.
마우스의 전원을 위해 AAA 건전지 2개가 필요한데, 나는 여타 제품과 같이 AA 한개일 줄 알고 AA를 준비했는데,, 이로인해 AAA 건전지를 따로 구해야 했다. ㅠㅠ..
건전지를 넣기 위해서는 마우스 바닥면에 있는 뚜껑(?)을 열어야 되는데, 위 사진처럼 시침 처럼 생긴 것을 왼쪽 파란 선까지 돌리면 열린다.
이후에 건전지를 넣어주면 된다. 건전지 사이에 보이는 동글이가 리시버다. 사용하고자 하는 데스크탑이나 노트북 등 USB를 꽂으면 연결된다.
본 제품은 기본적으로 블루투스 1대, USB 1대 총 2대의 연결만 지원하기에 비교대상이었던 로지텍의 타 제품은 3대 이상인걸 생각하면 아쉽긴 하지만, 12900원의 제품에서 그것 까지 바라는건 양심이 없는 것 같긴 하다.ㅋㅋㅋ
제품의 상단을 보면 마우스 휠 아래에 동그란 버튼이 있는데, 이 버튼이 바로 USB로 연결 할지, 블루투스로 연결할지 결정하는 버튼이다.
초록색은 USB 리시버, 파란색은 블루투스와 연결중이라는 의미다.
블루투스로 연결하려 했는데, 만약 제품이 안뜬다면 마우스에 있는 동그란 버튼을 꾹 누르면 깜빡깜빡 거리며 페어링 모드로 진입하게 되고, 이 때 블루투스 검색란에서 Mi Silent Mouse를 찾을 수 있다. (새로운 기기에서 블루투스 페어링을 할 때도 이렇게 하면 된다.)
마우스 버튼을 눌러보면 왼쪽 클릭과 오른쪽 클릭 버튼은 정말 무소음에 가까운 걸 알 수 있다. 다만 좌측에 존재하는 페이지 앞/뒤 버튼은 무소음이 아니니, 독서실 등 조용한 곳에서 사용할 떄 주의해야 할 것 같다.
총평 (★★★★☆ - 4.5개임 사실)
12900원 짜리 마우스가 이렇게 높은 퀄리티를 보여줄 줄이야! 생김새도 무난하고, 사용성도 무난하고!
다만 별 한개를 깎은 이유는 데스크탑에서 DPI 설정이 안된다. 너무 빠르다. (사실 별점 4.5개를 주고싶은데 반쪽 짜리 별은 없어서..)