Works by

Ren's blog

アプリケーションバックエンド中心に書いていきます

【Nuxt.js/TypeScript】Nuxt.js/TypeScriptでimport エイリアスを有効化する 

f:id:rennnosukesann:20190923222649p:plain:w250f:id:rennnosukesann:20190923222722p:plain:w350

Nuxt.jsでTypeScriptを使用した際、Webpackでよくやるimportエイリアスを適用したかったのでやってみました。

TypeScriptコンパイル時のエイリアスルールを設定

先ずはコンパイル時に怒られないよう tsconfig.tsエイリアスを設定します。

tsconfig.ts
{
  "compilerOptions": {
    ....
    "baseUrl": ".",
    "paths": {
      "@/*": ["./*"],
      "~/*": ["./*"]
    },
    ....
  }
}

baseUrl はimportの起点となるディレクトリです。
この場合 tsconfig.ts が配置されたところがルートディレクトリとなります。

paths に設定したいエイリアスを記述していきます。

nuxt.config.ts

TypeScript側では静的チェックを行うのみで、実際のコンパイル時にimportを解決してくれません。

なのでimport解決そのものはWebPackの設定で行います。

Nuxt.jsにおけるWebpackの設定は webpack.config.js(ts) ではなく nuxt.config.js(ts) で行います。

const path = require("path");
export default {
  ....
  resolve: {
    ....
    alias: {
      "@": path.resolve(__dirname),
      "~": path.resolve(__dirname)
    }
    ....
  },
  ....
};

上記のような alias プロパティにパスを設定していきます。

これでOK。TypeScriptコンパイルが通り、importを実行できます。

【yarn】yarnのパッケージキャッシュを参照・削除する

f:id:rennnosukesann:20180616080607p:plain:w300

yarnでインストールしたパッケージキャッシュを全て削除する場合、下記のコマンドを実施すればOK。

$ yarn cache clean

削除するパッケージキャッシュを指定する場合は以下。

$ yarn cache clean nuxt

なお、キャッシュされているyarnパッケージは、記コマンドで見れます。

$ yarn cache list
yarn cache v1.15.2
Name                                                     Version        Registry Resolved                                                                                                                                                                                    
@akryum/winattr                                          3.0.0          npm      https://registry.yarnpkg.com/@akryum/winattr/-/winattr-3.0.0.tgz#c345d49f8415583897e345729c12b3503927dd11                                                                                   
...

またキャッシュ格納先の参照は以下。

$ yarn cache dir
/Users/hoge/Library/Caches/Yarn/v4

参考文献

yarnpkg.com

コネクションプールサイジングでの適切な設定

Hikari CPの本家Githubで良Wikiを見つけたのでメモ。

github.com

コネクションプールの接続プール数は、以下の式で導出した数を初期値に調節していくのが良いとのこと。

プール数 =  CPUコア数 * 2 + スピンドル数

スピンドル数は、HDDなどディスク型ストレージ台数を表します。

そして、たとえ同時接続数10,000のサービスでも、DBへのコネクションプール数は上記の式で導かれた数程度までには抑えたほうがパフォーマンスが良いと述べています。

理由として、

  • 結局、DBサーバのCPUコアあたり1スレッドしか処理できないことを考えると(1スレッド=1コネクションと考え)、2スレッド以上はコンテキストスイッチによってパフォーマンスが低下する

  • とはいえ、I/O時間を考えると必ずしも1コア1スレッドでは最大パフォーマンスが出るわけではない

  • なので、シークによるI/O時間が発生するHDDの場合は、上記式から導出されるプール数を起点にすると良い

とのこと(大体の意訳)。

別に10,000同時接続でもコネクションプール10くらいでさばけるよ、むしろプール数増やしすぎるとパフォーマンス低下するよ、という内容ですね(問い合わせ内容にもよると思いますが)。

Wiki参照元でも述べらているように、SSDでは未検証らしいのですが、I/Oを考えないとなるともっと少なくなるのでしょうか。

またネットワーク通信の待ちを考慮していないので、厳密に考えるとそこも加味する必要はあります。

とはいえ、一つの参考にはなりそうです。

参考文献

github.com

【Vue/Vuetify】Vuetifyのv-text-fieldのclearableを有効にすると、削除時のmodelにはnullが挿入される

f:id:rennnosukesann:20190919210255p:plain:w200

VueのUIフレームワークVuetifyでは、テキストボックスである v-text-field に値削除ボタンを表示するオプションが clearable が指定できるのですが、この仕様が少し気になったのでメモします。


v-text-field に適当な文字列プロパティをバインドし、フォームの入力値とプロパティの値を連動させます。 

<div id="app">
  <v-app id="inspire">
    <v-col cols="12" sm="6" md="3">
      <v-text-field
            label="Clearable Form"
            v-model='text'
            clearable
            @input="inputed"
            ></v-text-field>
    </v-col>
    <v-col cols="12" sm="6" md="3">
      <p>text: {{ text === null ? 'null' : text }}</p>
    </v-col>
  </v-app>
</div>
new Vue({
  el: '#app',
  data: {
    text: ''
  },
  vuetify: new Vuetify(),
  methods: {
    inputed: function (){console.log(this.text);},
  },
})

See the Pen Vuetify Text Field Clear as null by Ren (@rennnosuke) on CodePen.


フォームに文字列を入力したとき、文字列クリアのための「✗」印が出現します。
これをクリックすることで、v-modelの変数も初期化されるのですが、この値はnullで初期化されます。

Vuetifyのissue上でも、少し話題にはなっている様子。

github.com

個人的には、モデルには任意のオブジェクトが設定できるわけで、故にnullになるのにはそこまで違和感はないかな、という感じなのですが
いかんせん文字列フォームということでうっかり空文字列が設定されると勘違いしちゃいそうですね。

上記issueでは 「クリア時の置換関数を定義できるようにしてはどうか」という意見も上がっていますが、
この機能は是非欲しいですね。

github.com

【Spring】Spring SecurityでCORS設定をする

f:id:rennnosukesann:20181220183020p:plain:w500

Spring SecurityでCORSを使用した設定を行う方法を紹介します。

Spring Securityを導入することで、Springアプリケーションに手軽に認証・認可を仕組みを設定することができますが、認可の一環としてCORSの設定も行うことができます。


例えば、以下のような yaml ファイルに記述したクライアントドメインを許可したいとします。

security:
  corsClientUrls:
    - http://localhost:3000
    - http://localhost:8080

このとき、 CORSは WebSevurityConfigurerAdapter 派生Beanクラスを定義して設定します。

なお上記yamlファイル上のドメイン@ConfigurationProperties アノテーションによって下記 SecurityConfig プロパティとして読み込まれ、 corsClientUrls に挿入されます。

@Configuration
@EnableWebSecurity
@ConfigurationProperties(prefix = "security")
class SecurityConfig : WebSecurityConfigurerAdapter() {
    
    // 許可するドメイン
    lateinit var corsClientUrls: List<String>

    override fun configure(http: HttpSecurity?) {
        http
            // ...
            // 諸々の設定
            // ...
            ?.and()
            ?.cors()
            ?.configurationSource(getCorsConfigurationSource()) //CORS設定
    }

上記コードでは、まず認証・認可について設定する WebSecurityConfigurerAdapter 派生クラスを定義し、メソッド configuration の引数 HttpSecurity にCORSのルールを設定しています。

具体的には、 HttpSecurity::cors()CorsConfigurer インスタンスを返し、この型が CorsConfigurer::configurationSource() メソッドを持っているのですが、このメソッドに CorsConfigurationSource インスタンスを渡してあげるとCORSについての設定をSpringのHTTPリクエスト・レスポンスに反映させることができます。

上記の例では、 getCorsConfigurationSource()CorsConfigurationSource インスタンスを返していることになります。

では、その getCorsConfigurationSource() の中身を見てみましょう。

    /**
     * CORS設定
     */
    fun getCorsConfigurationSource(): CorsConfigurationSource {

        val corsConfiguration = CorsConfiguration()
        
        // ①CORSを許可するURLの登録(Access-Control-Allow-Origin) 
        this.corsClientUrls.forEach { corsConfiguration.addAllowedOrigin(it) }

        // ②許可するHeaderの登録(Access-Control-Allow-Headers)
        corsConfiguration.addAllowedHeader(CorsConfiguration.ALL)
        
        // ③許可するMethodの登録(Access-Control-AllowMethods)
        corsConfiguration.addAllowedMethod(CorsConfiguration.ALL)
        
        // ④認証情報送信許可の登録(Access-Control-Allow-Credentials)
        corsConfiguration.allowCredentials = true

        val corsSource = UrlBasedCorsConfigurationSource()

        // どのパスに上記ルールを適用するか
        corsSource.registerCorsConfiguration("/**", corsConfiguration)

        return corsSource
    }

getCorsConfigurationSource() では、HTTPレスポンスヘッダ設定を CorsConfiguration インスタンスに詰めていき、
それを CorsConfigurationSource インスタンスに持たせています(実際には UrlBased orsConfigurationSourceを使用しています )。

上記コードでは、以下の4ヘッダーを設定しています。

①Access-Control-Allow-Origin: ...
②Access-Control-Allow-Headers: ...
③Access-Control-Allow-Methods: ...
④Access-Control-Allow-Credentials: ...

①にCORSを許可したいドメイン(オリジン)を、
②にCORS中で許可したいHTTPヘッダを、
③にCORS中で許可したいHTTPメソッドを、
④にCORS中での認証情報(BASIC認証情報やクッキーなど)の受け渡し許可有無を、
設定します。

参考文献

docs.spring.io

【Spring】Spring AOP - @AfterThrowing/@Around で例外を再スローする

f:id:rennnosukesann:20181220183020p:plain:w500

Springアプリケーション内で発生する例外をSpring AOPでハンドリングする方法を紹介します。

ある特定の例外を別の例外として再スローするシーンがたまにあるのですが、SpringではこのようなときにAOPによる横断的な例外キャッチを使うと便利です。

例えばDDDを意識したレイヤアーキテクチャをSpringで実装する場合、ドメイン層で投げられる例外をアプリケーション独自の例外にして投げ直したい・・・といった場合に有効です。

Usage

AOPの導入についてはこちらが詳しいです。

www.baeldung.com

導入後、まずは Aspect に相当するクラスを定義します。

@Aspect
@Component
class ExceptionAspect {

}

Aspect 定義後、 @AfterThrowing アノテーションを付与したメソッドを定義します。
@AfterThrowing は、引数に取る対象メソッド内で例外送出が発生した場合に呼び出されます。
なおこの例外送出は @AfterThrowing 内で握りつぶし、正常終了とすることはできません(再例外送出による例外上書きは可能)。

@Aspect
@Component
class ExceptionAspect {

    @AfterThrowing("execution(* com.rk2.rknikki.domain..*.*(..))", throwing = "e")
    fun handleException(joinPoint: JoinPoint, e: Throwable): Nothing {
        when (e) {
            is AuthenticationException -> throw UnauthorisedException(e.message ?: "", e)
            else -> throw e
        }
    }

}

@AfterThrowing の引数に指定しているのはPointCut式と呼ばれる指定子で、対象メソッドを指定するためのものです。
ここでは、 com.rk2.rknikki.domain パッケージ配下の、任意の返り値・引数を持つすべてのメソッドを対象としています。

www.baeldung.com

joinPoint は対象メソッドの情報を保持しており、対象メソッドの引数ラベルや値などの情報を取得できます。

また上記の処理は、 @Around アノテーションを使用して以下のように書くこともできます。

@Around アノテーションを使用することで、対象メソッドの前後に処理を挟むことができます。
try-catch等による例外の握りつぶしも可能です。

@Aspect
@Component
class ExceptionAspect {

    @Around("execution(* com.rk2.rknikki.domain..*.*(..))")
    fun handleException(jp: ProceedingJoinPoint): Any? {
        try {
            return jp.proceed()
        } catch (e: Throwable) {
            when (e) {
                is AuthenticationException -> throw UnauthorisedException(e.message ?: "")
                else -> throw e
            }
        }
    }

}

jp.proceed() は対象メソッドそのものの処理を実行します(引数は不要)。

参考文献

www.baeldung.com www.baeldung.com

【Vue.js】$onによる同一イベントへのハンドラ登録は、上書きではなく追加形式になる

f:id:rennnosukesann:20181217223208p:plain:w300

jQueryと同様に、Vueコンポーネントへハンドル可能なイベントを登録する組み込みのメソッド $on は、同一イベントに対し複数回ハンドラ登録を行うと、その全てがハンドラとして有効になります。

function hello () {
    console.log('hello.');
}

function yee () {
    console.log('yee.');
}

const eventBus = new Vue();
eventBus.$on('hello-event', hello);
eventBus.$on('hello-event', yee);

eventBus.$emit('hello-event')
// hello.
// yee.

上記の例では、 $on を使って同じイベントに異なるハンドラを続けて登録しています。
このような場合、 helloyee によって上書きされず、 両方のハンドラが hello-event に対するハンドラとして呼ばれるようになります。

上書き形式でハンドラの登録を行いたい場合、 $off を使用して一旦イベントハンドラを削除し、再度 $on でハンドラを登録し直します。

eventBus.$off('hello-event');
eventBus.$on('hello-event', yee);