SAL-KI by LIM KIM + Vocaloid Cyber Diva

미스틱 엔터테인먼트와 계약 종료 후 3년만에 돌아온 김예림, 아니 림킴은 마치 지옥에서 돌아온 듯 살기가 서렸다. 림킴을 비롯하여 장재인, 퓨어킴, 박지윤(가수)는 물론 박지윤(MC)까지 미스틱만 들어가면 잘 나가던 분들도 잘 안되는데, 그게 미스틱이 프로듀싱을 잘 못해서 그런 것 같아서 안타깝다.

이번에는 림킴이 직접 프로듀싱을 해서 새로운 출발을 했는데, 나도 응원하는 의미에서 팬메이드 음악을 올렸다. 보컬로이드 보컬들 중에서 가장 덜 모에스러운 보컬을 골랐지만, 림킴의 헤비메탈 스러운 독기를 표현하기에는 많이 부족했다. 힙합이라서 흘려 발음하는 부분도 많아서 고생했지만 나는 꽤 즐거운 경험이었다. 예를 들어

Not a young average school girl

같은 경우 average는 원래 3음절이지만 av’rage로 줄여서 2음절로 발음하고, ge + sch는 자음이 3개 이상 이어지기 때문에 가운데 자음들이 묵음이 되어 (맨 처음 자음과 마지막 자음 2개만 남아) ge + ch로 발음된다. 이런 건 보컬로이드에서 자동으로 안 해주기 때문에 내가 직접 해야 했다. (아차, 말은 이렇게 해놓고 정작 노래에서는 자음 3개를 다 살렸네 😅)

내가 즐거웠던 만큼 듣는 여러분도 즐겁기를 바란다.

Google ReCAPTCHA to AngularJS

Why?

To block robots.

Other captchas?

JavaLite – captcha http://javalite.io/captcha
But I wanted not to challenge any images to users. Google ReCAPTCHA v3 only reads users’ mouse and keyboard interactions and challenge nothing to find out robots.

Is it free?

Yes, commercially free. But Google will collect all users’ interactions all over the website, right on the document.window! If you block this, ReCAPTCHA will not work. (I’ve tested a lot)
I personally think why Google provides this captcha service free is that Google wants to improve AdSense quality using ReCAPTCHA’s data. You know there are a lot of abuses of AdSense and it is a direct threat to Google’s revenue.
And you know there is no destroy() function on it. Once it is on, you cannot remove it. It keeps tracking users’ experience until they go out that page. If you made your website as one unified page, you cannot remove it until refreshing(F5) or logging out. (I’ve also tested a lot)

Sign up & get API keys

https://developers.google.com/recaptcha/
Site key: something (will share with users)
Secret key: something (never share with users)

Add to the client side

import { Directive, OnInit } from '@angular/core';
@Directive({
selector: '[myRecaptcha]'
})
export class MyRecaptchaDirective implements OnInit {
ngOnInit() {
const script = document.createElement('script');
script.src = 'https://www.google.com/recaptcha/api.js?render=' + environment.recaptchaSitekey;
document.body.appendChild(script);
}
}

<div myRecaptcha></div>

import { ... NgZone } from '@angular/core';
declare const grecaptcha: any;
declare global {
interface Window {
buySomething: any;
}
}
constructor(

private ngZone: NgZone
) {

}
ngOnInit() {
window.buySomething = window.buySomething || {};
window.buySomething.namespace = window.buySomething.namespace || {};
window.buySomething.namespace.callbackBuySomething = this.callbackBuySomething.bind(this);
}
clickBuySomething() {
grecaptcha.execute(environment.recaptchaSitekey, {action: 'buy_something'}).then(function(token: string) {
// console.log('grecaptcha.execute() token=' + token);
window.buySomething.namespace.callbackBuySomething(token);
});
}
callbackBuySomething(token: string) {
this.ngZone.run(() => this.saveSomething(token));
}
saveSomething(token: string) {
//call API with token
//then API will throw it is valid or not
}

Add to the server side

@Data
public class GoogleRecaptchaResponse {
Boolean success;
Double score;
String action;
LocalDateTime challenge_ts;
String hostname;
@JsonProperty("error-codes") String[] errorCodes;
}


url: https://www.google.com/recaptcha/api/siteverify
secretKey: something
scoreThreshold: 0.5 #1.0 is very likely a good interaction, 0.0 is very likely a bot

public boolean verifyRecaptcha(String token, String action) {
GoogleRecaptchaResponse gResponse = restTemplate.postForEntity(
URI.create(String.format(url+"?secret=%s&response=%s",secret,token)),
null,
GoogleRecaptchaResponse.class).getBody();
if(gResponse!=null &&
gResponse.getSuccess() &&
gResponse.getScore() > scoreThreshold
&& gResponse.getAction().equals(action)) {
log.info("ReCAPTCHA verification succeeded: {}",gResponse);
return true;
}else {
log.error("ReCAPTCHA verification failed: {}",gResponse);
return false;
}
}

How to test

Postman (just call API) https://www.getpostman.com/
JMeter (record & play) https://jmeter.apache.org/
Selenium WebDriver (build a macro & run) https://www.seleniumhq.org/
…any other suggestions?

References

https://netbasal.com/how-to-integrate-recaptcha-in-your-angular-forms-400c43344d5c
https://stackoverflow.com/questions/35296704/angular2-how-to-call-component-function-from-outside-the-app

Install MariaDB on Mac

brew

brew update
brew install mariadb

permission

chmod 777 /usr/local/etc/my.cnf.d

set PATH

cd ~
touch .bash_profile
vi .bash_profile
export PATH=${PATH}:/usr/local/Cellar/mariadb/10.3.15/support-files:/usr/local/Cellar/mariadb/10.3.15/bin
(change 10.3.15 as your version!)

mysql start

brew info mariadb
To have launchd start mariadb now and restart at login:
brew services start mariadb
Or, if you don’t want/need a background service you can just run:
mysql.server start

mysql shell

mysql -u root
show databases;
select table_name from information_schema.tables where table_schema=’mysql’;
use mysql;
exit

mysql workbench

https://dev.mysql.com/downloads/workbench/
Preferences -> SQL Editor and then check the box “Show Metadata and Internal Schemas”

Switched to Azure

요즘 클라우드가 다들 1년 무료라서, 돌아가면서 쓰고 있다. Amazon AWS에서 1년, Google Cloud에서 1년, 그리고 Microsoft Azure로 옮겨서 1년을 시작했다.

근데 막상 내가 작도닷넷을 옮겨보니 왜 1년이나 무료로 주는지 알만했다. 옮기는게 정말 어려웠다. 그냥 있는 그대로 옮기는 것도 엄청 어려웠다. 같은 클라우드 서비스라고 해도 아마존, 구글, 마이크로소프트가 각각 사용방법이 달랐고, 새로운 기능이 출시되고 보안이 강화된 부분이 있어서 찾아보고 공부해서 적용하는데 한참 걸렸다.

워드프레스는 돌아가지만 아직 태터툴즈, 제로보드4는 돌아가지 않는다. 차차 조치하도록 하겠다.

1. Azure에 가입하고 resource group을 만든다. location은 westus2가 제일 싸고 마침 내가 미국 서부에 있어서 여기로 정했다. 이름은 대소문자를 섞어 써도 되지만, (여기선 아니지만 key vault를 썼을 때 대소문자를 섞어 쓰면 URI를 인식하지 못하는 문제로 하도 고생을 해서) 소문자만 쓰기로 했다. (이런 사소한 문제가 사람 힘들게 한다)
az group create --name xacdonet --location westus2

2. Free tier에 맞춰 VM을 만든다. Azure는 다른 클라우드 서비스와 달리, 자기네가 정해놓은 resource들만 1년간 공짜로 지원한다. 나는 처음에 돈 아끼려고 disk를 SSD가 아니라 HDD로 만들었다가 한 40센트 정도 돈이 나간 다음에야 아차 싶어서 바꿨다. (SSD도 Standard가 아니라 Premium이 무료다) VM도 제일 작은 B1ls가 무료가 아니라 그것보다 하나 큰 B1s만 무료다. 여러분이 나와 같은 짠돌이라면 꼭 무료가 무엇인지 확인하고 VM을 만드시길 바란다.
az vm create --resource-group xacdonet --name xacdovm --image debian --size Standard_B1s --storage-sku Premium_LRS --os-disk-size-gb 63

3. SSH Key를 만든다. Azure CLI에서 SSH Key를 만들수도 있지만, Azure에 종속되기 싫어서 PuTTYgen으로 내 컴퓨터에서 SSH Key를 만들어 썼다.
az vm user update --resource-group xacdonet --name xacdovm --username kyungwoo --ssh-key-value xacdoPublicKey.txt
az vm user reset-ssh --resource-group xacdonet --name xacdovm
az vm user update --resource-group xacdonet --name xacdovm --username kyungwoo --password password

4. Azure Cloud shell에서 SSH로 VM에 접속한다. Google 클라우드도 그랬지만 이렇게 하면 내 컴퓨터에 아무것도 설치하지 않아도 다 클라우드에서 할 수 있기 때문에 편리하다. 아참 위에서 빼먹었는데, Azure cloud shell에 SSH Key, html 압축한 거, sql 백업한 것 등을 미리 올려놓고 ssh나 scp로 전송하면 편하다. (이러는게 Putty나 Filezilla 등으로 접속하는 것보다 보안상 좋아서 그런가?) 참고로 여기 올려놓는 것도 조금이나마 돈이 나가고 1년 무료에 포함되지 않기 때문에, 필요한 만큼만 잠깐 썼다가 지우면 돈을 아낄 수 있다.
ssh -i xacdoPrivateKey.pem kyungwoo@1.2.3.4
scp -i xacdoPrivateKey.pem $1 kyungwoo@1.2.3.4:/home/kyungwoo/$2

이런 명령어들은 .sh 파일로 만들어놓으면 편하다.

5. Apache를 설치하고, 전에 쓰던 html을 복사한다.
sudo apt update && sudo apt upgrade && sudo apt autoremove && sudo apt autoclean
sudo apt install apache2 apache2-utils
systemctl status apache2
sudo apache2 -v
sudo apt install unzip
sudo rm -Rf /var/www/html
sudo unzip html.zip -d /var/www
sudo chown www-data:www-data /var/www/html/ -R
sudo systemctl start apache2
sudo systemctl enable apache2

6. mariadb를 설치하고, 전에 쓰던 데이터를 붓는다. Azure에서 MariaDB, MySQL을 제공하긴 하지만, 그러면 아무리 싸도 한달에 25달러는 더 내야 해서 돈 아끼려고 그냥 VM에다 깔았다. 이러면 가끔씩 sudo apt upgrade도 해주고, mysqldump도 떠서 어디다가 저장해야 한다.
(전에 쓰던 데서)
mysqldump xacdo > xacdo.sql
mysqldump wordpress > wordpress.sql
(새거에서)
sudo apt install mariadb-server mariadb-client
systemctl status mariadb
sudo mysql
create database wordpress;
create database xacdo;
exit;
sudo mysql xacdo < xacdo.sql
sudo mysql wordpress < wordpress.sql
sudo systemctl start mariadb
sudo systemctl enable mariadb
sudo mysql_secure_installation
mysql: root / password
sudo mariadb -u root
sudo mysql_upgrade --verbose -u root -p
mariadb --version

참고로 B1ls에서 RAM이 512M밖에 안되서 mariadb가 안 뜨면 performance schema를 꺼주자. (B1s는 RAM이 1G라서 굳이 안 꺼도 된다.)
sudo vi /etc/mysql/my.cf
[mysqld]
performance_schema = off


참고로 B1ls에서 RAM이 512M밖에 안되서 mariadb가 안 뜨면 performance schema를 꺼주자. (B1s는 RAM이 1G라서 굳이 안 꺼도 된다.)
sudo vi /etc/mysql/my.cf
[mysqld]
performance_schema = off

7. PHP를 설치한다.
sudo apt install php7.0 libapache2-mod-php7.0 php7.0-mysql php-common php7.0-cli php7.0-common php7.0-json php7.0-opcache php7.0-readline
sudo a2enmod php7.0
sudo systemctl restart apache2
php --version

8. WordPress를 설치한다. 이건 아까 /var/www/html 옮길때 같이 했어야 했는데, 까먹고 안 옮겨서 그냥 새로 설치했다. 덕분에 이미지 파일들이 다 날아갔다. (난 Export하면 당연히 이미지까지 같이 될 줄 알았다. 여러분은 나같은 실수 하지 마시길.)
wget https://wordpress.org/latest.tar.gz
tar xpf latest.tar.gz
rm latest.tar.gz
mv wordpress wp
sudo chown www-data:www-data /var/www/html/wp -R
sudo mysql -u root -p
use mysql;
update user set plugin='' where User='root';
flush privileges;
exit

아참, 파일/폴더 권한을 더 넣어줘야 한다.
sudo -u www-data find . -type d -exec chmod 755 {} \;
sudo -u www-data find . -type f -exec chmod 644 {} \;

Image crop이 안되면 뭘 더 설치해야 한다.
sudo apt-get install php7.0-gd
sudo apachectl restart

9. SSL 인증서를 설치한다. 요즘 Chrome에서 SSL 안쓰면 “Not secure(안전하지 않음)”이라고 찝찝하게 뜨길래, 귀찮지만 SSL을 깔아줬다. 그렇다고 돈을 들이긴 싫어서 무료인 Let’s Encrypt를 깔았다. 이게 끝이 아니고, 3개월마다 (귀찮게) 갱신해줘야 한다.
sudo a2enmod ssl (이걸 미리 해줘야 한다. 안그러면 뭔가 안됨)
sudo apt-get install git
git clone https://github.com/letsencrypt/letsencrypt
cd letsencrypt
./letsencrypt-auto

여기서 apache SSL 설정이 자동으로 안되면 수동으로 해야지, 별 수 있나.
sudo find /var/lib/waagent/ -name "*.prv" | cut -c -57 (인증서 ID가 나옴)
sudo ls /var/lib/waagent (.crv, .prv 파일이 나옴)
cd /etc/apache2/sites-available
sudo vi default-ssl.conf (위에서 찾은 .crv, .prv 파일을 넣어줌)
sudo a2enmod ssl (여기서 아차 싶었다. 다음엔 까먹지 말고 이걸 미리 해야지 싶어서 위에다가 추가했다.)
sudo a2ensite default-ssl.conf
sudo systemctl restart apache2
sudo apache2ctl -S

10. 잘 떴나? Port를 확인한다.
sudo lsof -i -P -n | grep LISTEN
sudo netstat -tulpn | grep LISTEN

3D Drum Room


red: cabasa
blue: synth tom
X-axis: stereo panning (left to right)
Y-axis: velocity (light to strong)
Z-axis: FX send (reverb intensity)
I made a conceptual “drum room” in the 3-dimension space. In this virtual space, I can move two spheres with a mouse, and they interact with their environment and user behavior. They dynamically generate 3-dimensional sound with different velocity, stereo panning, and a reverb effect. source codes: https://github.com/kyungwoh/3d_drum_room

메타 예능 – 두니아 (2018, MBC)

미술 평론가 임근준메타(meta-)라는 말을 많이 썼다. 메타는 그 자체에 대한 것, 자기 참조적인 것을 말하는데, 예를 들어 영화 나이트메어 7 – 뉴 나이트메어(1994)에서 나이트메어 영화를 만드는 얘기를 영화 안에서 다루는 것 같은 것이다.
내 생각에는 포스트(post- = 후기), 비욘드(beyond = ..너머) 다음으로 하다하다 더 이상 새로운 것을 만들기가 어려울 때 메타(meta- =자기 참조)까지 가는 것 같다.
나는 두니아(2018)가 메타 예능이라고 생각한다. 두니아는 예능 프로그램의 형식에 익숙해야 웃을 수 있는 부분이 많다. 예를 들어 미리 설정된 드라마 부분과, 연기자의 즉흥에 맡기는 리얼리티 부분이 계속 교차되는데, 이 두 부분의 경계가 때론 명확하지 않고, 심지어는 와장창 깨져버리는 경우도 있다. 이것을 두니아에서는 재미의 요소로 삼았는데, 이것은 예능이나 드라마라는 형식에 익숙하지 않다면 “이게 도대체 뭐지?”하고 이해하지 못할 수 있는 어려운 장치다.
나는 예능 프로그램을 매우 좋아하기 때문에, 두니아를 아주 재미있게 보았다. 초반 두 달 정도는 연기자들도 시청자들도 두니아의 형식에 익숙하지 않아서 많이 헤맸지만, 세 달을 넘어가니 출연자들도, 보는 사람들도 익숙해져서 형식을 마음껏 가지고 놀 수 있게 되었다. 그래서 비로소 재미있을 수 있었다.
예능에 대한 예능, 메타 예능. 이런 시도가 나는 참 새로웠다. 그러고보니 나는 옴부즈맨 프로그램도 항상 재미있게 보았다. 방송사가 자사 방송에 대해 스스로 비판한다는 것이 재미있었다. 두니아도 예능이 예능 스스로를 재미로 삼는다는 점이 좋았다.
시청률이 너무 낮아서 아쉽게도 추석 개편 때 끝날 것 같은데, 다음에는 굳이 외국을 나가지 말고, 쾌적한 스튜디오에서 출연자과 연출자들이 자신의 역량을 마음껏 발휘할 수 있도록, 너무 습하지 않고 너무 덥지 않은 환경에서 생산성 높게 방송을 찍었으면 좋겠다. 특히 초반에는 정말 너무 더워서 지쳐 보였다.
예능이 출연자들을 괴롭혀야 재밌는 경우도 있지만, 출연자들을 편안하게 배려해줘야 재밌는 경우도 있다. 마리텔 같은 경우도 서로 개입하고 시끄럽게 경쟁해서 재밌는 사람도 있었지만, 자기 집에서 혼자 유튜브 방송을 하듯이 조용히 집중해야 더 잘 되는 사람도 있었다.
나는 효리네 민박 – 시즌1 – 2화 – 남편 상순에게 털어놓는 조금은 외로웠던…’스물다섯 이효리’에서 이효리가 자신의 과거를 회상하는데 이효리가 아닌 타인의 정면샷들이 추억처럼 지나가며 음악 없이 가니까 적막하고 쓸쓸해서, 마치 아피찻퐁 위라세타쿤의 영화를 보는 것 같았다. 나는 이렇게 예능이 예능을 넘어서는 순간들을 보곤 한다. 가짜 웃음소리도 없고 음악까지도 멈춰버린 조용한 예능도 가능하다. 꼭 메타 예능이 아니더라도, 비욘드 예능도 가능하지 않을까.나는 그런 순간을 기다리며 두니아를 계속 본다. (끝)

트루스 오어 데어 (Truth or Dare, 2018)

출발 비디오 여행이었나? 정확히 기억나지는 않는데 하여튼 출발 비디오 여행 같은 TV 영화 소개 프로그램에서 트루스 오어 데어라는 영화 소개를 봤다.  트루스 오어 데어는 술자리에서 하는 진실 게임 + 왕 게임 같은 건데, 돌아가면서 자신의 재미있는 진실을 고백하던가, 그러기 싫으면 짖궂은 벌칙을 하는 게임이다.
이 영화에서는 젊은 친구들이 트루스 오어 데어 게임을 하는데, 이게 아무리 해도 끝나지가 않는 거다. 그리고 벌칙을 너무 위험한 걸 시켜서 죽기도 한다. 공포영화니까… 그래서 이 영화의 주인공들은 죽기 싫어서 이 게임에서 빠져나오려고 한다. 영화 소개는 여기까지. (TV 영화 소개도 여기까지였다)
(이 다음부터는 스포일러입니다) 여기까지 보면 꽤 흥미로워서 인터넷을 찾아봤는데, 결말은 좀 시시했다. 게임을 시작하면 저주를 받아서, 죽지 않는 한 빠져나가는 방법이 없었다. (있긴 했는데 잘 안되게 되었다) 결국 죽을 때까지 게임을 계속 하는 수밖에 없었다. 그럼 결국 다 죽어야 하나? 그래서 주인공들은 마치 한국의 3년 고개 같은 아이디어를 떠올린다.
3년 고개가 뭐냐 하면, 한 번 넘어지면 3년밖에 살지 못한다는 저주받은 고개가 있었다. 그래서 어떤 사람이 이 고개에서 넘어져서, 나는 이제 3년밖에 못 살겠구나 슬퍼했는데, 잘 생각해보니 3년마다 한번씩 넘어지면 계속 살겠구나 싶은 것이었다. 그래서 3년마다 일부러 넘어지며 오래오래 잘 살았다는 얘기다.
이 트루스 오어 데어 게임도 마찬가지인게, 한 사람씩 돌아가면서 하는 거니까, 참여하는 사람들을 엄청나게 많이 늘리면 자기 차례가 띄엄띄엄 돌아오게 된다. 그러므로 참여자가 많을수록 오래 살 수 있다. 그래서 얘네들은 유튜브로 이 게임을 생중계해서 전 세계 사람들을 게임으로 끌어들인다. 오올~ 머리 좋네. 그렇긴 한데 좀 김이 빠지긴 한다. (왜냐하면 이러면 더 이상 공포영화가 아니니까)
물론 3년 고개에도 “최초 넘어진 시점부터 3년 후에 죽는다” 같은 악성 조항을 넣으면 이런 트릭이 안 먹힐 수 있다. 트루스 오어 데어 게임도 “이 게임은 최대 12명까지만 참여가 가능하다” 또는 “참여자가 늘어나도, 다음 차례는 12일/(전체 참여자 수) 시간 후 자동으로 넘어간다 (예) 참여자가 120명인 경우, 한 사람이 12일/120명=2시간을 초과하면 그 사람은 현재 도전(트루스 오어 데어)을 계속 하면서 다음 사람에게 차례가 넘어간다” 같은 악성 조항을 빡빡하게 추가하면 이런 트릭이 안 먹힐 것이다. (물론 이래도 더 이상 공포영화가 아닐 것이다) (조항을 따지다 보면 변호사가 필요할지도 모른다)
좀 더 나아가서, 만약 나라면 이런 상황에서 어떡할 것인가? 나라면 공포에 사로잡히기보다는 일단 상황을 받아들이고, 내일 지구의 종말이 오더라도 한 그루의 사과나무를 심겠다는 마음으로, 내 생애 마지막에 뭘 하고 죽으면 좋을까 진지하게 고민할 것이다. (이제 공포영화는 물 건너 갔다) (이쯤 되니 영화 멜랑콜리아(2011)에 가까워진다)
예를 들면 이런 식으로, 여러분, 나는 이제 죽음을 겸허하게 받아들이기로 했습니다. 마지막으로 여러분께 고백합니다. 제가 지금까지 감춰온 진실은, 사실 저는 이상성애자였다는 것이었습니다. 더 늦기 전에 말하고 싶었습니다. 제 최애는 비욘세가 아니라 제이지였습니다. 정말 죄송합니다. 그리고, 사랑했습니다… 아니 제이지 말고 당신을요.
다음 차례에 저는 더 이상 고백할 진실이 없습니다. 제 인생을 아무리 돌이켜봐도 더 이상 감출 비밀이 없습니다. 그러므로 여러분, 저는 이제 죽음의 도전을 맞이해야 합니다. 마지막으로 저를 아껴주신 여러분께 감사의 말씀을 드리며, 제가 여러분께 마지막으로 선물을 남기고자 합니다. 제가 음원 서비스에 올린 노래들의 수익금, 모두 가지십시오. 작도닷넷의 광고 수익금도 마찬가지로 모두 가지십시오. 얼마 되지는 않지만 좋은 곳에 써 주시기 바랍니다. 여기에 아이디와 패스워드를 남깁니다. 부디 안녕히…
(다음 장면) 그의 장례식, 그의 음원 수익금 정산서, 그의 애드센스 수익금… 슬퍼하는 사람들. 그리고 참가자들이 모두 죽어서 마침내 끝난 이 죽음의 게임. 갑자기 닥쳐온 죽음을 담담하게 맞이한 주인공들의 마지막 모습들. 이것을 우리는 삶이라 불러야 하지 않을까, 죽음이라 부르기 보다는. (끝)