# Getting Started

## Configuration
### Logging
```
JpaAndRequestLoggingConfig.java : JPA / Web
    P6spySqlFormatConfiguration.java - decorator.datasource.p6spy.enable-logging: true
    CustomCommonsRequestLoggingFilter.java - org.springframework.web.filter: debug 필수
    decorator.datasource.p6spy
    
Security 비활성
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class, UserDetailsServiceAutoConfiguration.class})
SecurityJavaConfig.java - 예외 등록
 - h2, swagger

Swagger 설정 : SwaggerConfig

resources 설정
  WebAndFluxCommonConfig.java - MVC / Flux 공통 사용  

MVC / Flux 동시 사용 설정
  @EnableWebMvc 만 설정 - @EnableWebFlux는 미사용(사용시 충돌)
  MVC application
      - @EnableWebMvc
      - @SpringBootApplication : exclude에 WebMvcAutoConfiguration.class    
```

### swagger
```java
    //파라메터 값을 모두 String으로 취급
    //String이 아닌 경우 example 값을 부여해야 compile시 WARN 발생 안함
    @ApiImplicitParam(name="cmmUserId", value="사용자ID-PK", example = "1")

```

### Reference Documentation
For further reference, please consider the following sections:

* [Official Apache Maven documentation](https://maven.apache.org/guides/index.html)
* [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/2.5.0/maven-plugin/reference/html/)
* [Create an OCI image](https://docs.spring.io/spring-boot/docs/2.5.0/maven-plugin/reference/html/#build-image)

# **Tailwind CSS**
- [Tailwind CSS](https://tailwindcss.com/): 경량의 **CSS** 프레임워크이다. 덩치가 큰 **Bootstrap**과 달리 **CSS**로만 작동하면서 사용자에게 **UI** 구현을 맡기는 컨셉이다. 구현이 직관적이고 자유도가 높아 최근 급속도로 생태계가 확장되고 있다.
- [Tailwind Cheat Sheet](https://nerdcave.com/tailwind-cheat-sheet): **Tailwind CSS**의 모든 클래스를 한 눈에 조회 검색 가능한 사이트이다. 사용이 편리하여 레퍼런스 사이트보다 더 찾게 된다.
- [Tailwind Toolbox](https://www.tailwindtoolbox.com/): **Tailwind CSS** 기반으로 **UI** 구현에 참고할만한 여러 완성 템플릿을 제공한다.
- [Tailwind Components](https://tailwindcomponents.com/): **Tailwind CSS** 기반으로 **UI** 구현에 참고할만한 여러 완성 컴포넌트를 제공한다.
- [Awesome Tailwind CSS](https://github.com/aniftyco/awesome-tailwindcss): **Tailwind CSS** 관련 유용한 유용한 정보들을 소개한다.
- [We build a login using TailwindCSS](https://stefanbauer.me/building-pingping/we-build-a-login-using-tailwindcss): **Tailwind CSS**을 이용한 로그인 폼 제작 과정을 설명한다.

# **Webflux 지원 Database**
1. Postgres (io.r2dbc:r2dbc-postgresql)
2. H2 (io.r2dbc:r2dbc-h2)
3. Microsoft SQL Server (io.r2dbc:r2dbc-mssql)
4. MySQL (com.github.mirromutth:r2dbc-mysql)
5. jasync-sql MySQL (com.github.jasync-sql:jasync-r2dbc-mysql)

### mysql 사용시
```xml
<dependency>
    <groupId>io.r2dbc</groupId>
    <artifactId>r2dbc-pool</artifactId>
    <version>0.8.0.RELEASE</version>
</dependency>

<dependency>
    <groupId>io.r2dbc</groupId>
    <artifactId>r2dbc-pool</artifactId>
    <version>0.8.0.BUILD-SNAPSHOT</version>
</dependency>

<dependency>
    <groupId>dev.miku</groupId>
    <artifactId>r2dbc-mysql</artifactId>
    <version>0.8.1.RELEASE</version>
</dependency>
```
### sample
```java
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.HashMap;

import javax.annotation.PostConstruct;
import org.springframework.stereotype.Repository;
import io.r2dbc.pool.ConnectionPool;
import io.r2dbc.pool.ConnectionPoolConfiguration;
import io.r2dbc.spi.ConnectionFactories;
import io.r2dbc.spi.ConnectionFactory;
import io.r2dbc.spi.Result;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Repository
public class DatabaseConfig {
    private ConnectionFactory factory;
    private ConnectionPool pool;

    //연결
    @PostConstruct
    public void init() {
        factory = ConnectionFactories.get("r2dbcs:pool:mysql://비번:아이디@주소:포트/db명");
        ConnectionPoolConfiguration configuration = ConnectionPoolConfiguration.builder(factory)
                .maxIdleTime(Duration.ofMillis(1000))
                .maxSize(20)
                .build();
        pool =  new ConnectionPool(configuration);
    }

    //단일 셀렉트, 조회할 테이블 데이터가 n개이면 이런식으로 쓰면 안된다.
    public Mono<HashMap<Object, Object>> selectSingle() {
        Mono<HashMap<Object, Object>>  mono = Mono.from(pool.create()).map(connection ->  //커넥션 가공
                Flux.from(connection.createStatement("select name, desc, date from test where id_=?ids").bind("ids", "2").execute())
                        .concatMap( result-> //1차 변환
                                result.map((row, rowMetadata)-> {  //결과 재 조립 후 리턴
                                    HashMap<Object, Object> item = new HashMap<>();
                                    item.put("names", row.get("name",String.class));
                                    item.put("desc", row.get("desc",String.class));
                                    item.put("date", row.get("date",Object.class));
                                    return item;
                                })
                        ).doFinally( (st)->{connection.close();})
        ).flatMap( ccc -> Mono.from(ccc));  //2차 변환
        return mono;
    }

    //단순 셀렉트 예제
    public Flux<HashMap<Object, Object>> selectMany() {
        Flux<HashMap<Object, Object>>  flux = Flux.from(pool.create()).concatMap(connection ->  //커넥션 가공 및 1차 변환
                Flux.from(connection.createStatement("select name, desc, date from test where id_=?ids").bind("ids", "2").execute())
                        .concatMap( result-> //2차 변환
                                result.map((row, rowMetadata)-> {  //결과 재 조립 후 리턴
                                    HashMap<Object, Object> item = new HashMap<>();
                                    item.put("names", row.get("name",String.class));
                                    item.put("desc", row.get("desc",String.class));
                                    item.put("date", row.get("date",Object.class));
                                    return item;
                                })
                        ).doFinally( (st)->{connection.close();})
        );
        return flux;
    }

    //저장 예제
    public Mono<? extends Result> insertTest() {
        return Mono.from(pool.create()).map( con ->
                Mono.from(
                        con.createStatement(" insert into test(name, desc, date) values (?name, ?desc, ?date) ")
                                .bind("name", "abcd").bind("desc", "desc").bind("date", LocalDateTime.now()).execute()
                ).doFinally( st-> con.close()).map( result -> result)
        ).flatMap( c-> Mono.from(c));
    }
}
```
