1. pull or Merge 취소
실수로 pull 받아 버린 경우
git reset --hard ORIGIN_HEAD
2. SFTP 서버제공 사이트
이번에 프로젝트에서 SFTP 를 사용했는데, 테스트시 SFTP 원격 서버가 필요했는데, 제공하는 사이트가 있었다.
https://www.sftp.net/public-online-sftp-servers
3. CONTENT_DISPOSITION
http 응답 헤더에 CONTENT_DISPOSITION 옵션값은 사용자가 응답받은 데이터를 어떻게 표시할 것인지를 정의한다.
기본값은 inline 이며 화면에 표시해준다.
attachment 는 사용자가 로컬에 다운받을 수 있게 해준다.
val httpHeaders = HttpHeaders().apply {
this[HttpHeaders.CONTENT_DISPOSITION] = "attachment; filename=$newFileName"
}
4. Async
스프링에서 메서드에 @Async 에너테이션을 붙이면 그 메서드는 비동기 처리가 된다.
그에 앞서, 비동기 처리를 4가지 방식으로 짜 보았다.
- 그냥 Thread 객체를 이용한 방법
- ExecutorSerivce 를 이용한 방법
- @Async 에너테이션을 이용한 방법
- @Async 에너테이션 + ThreadPoolTaskExecutor <- 이 방법이 제일 좋다.
@Service
class MultiThread {
val logger = LoggerFactory.getLogger(MultiThread::class.java)
fun printNumber(x: String) {
Thread {
Thread.sleep(SplittableRandom().nextLong(3000))
logger.info("[${Thread.currentThread().name}] $x")
}.start()
}
fun printNumber2(x: String) {
val executorService: ExecutorService = Executors.newFixedThreadPool(10)
executorService.submit {
Thread.sleep(SplittableRandom().nextLong(3000))
logger.info("[${Thread.currentThread().name}] $x")
}
}
@Async
fun printNumber3(x: String) {
Thread.sleep(SplittableRandom().nextLong(3000))
logger.info("[${Thread.currentThread().name}] $x")
}
@Async("threadPoolTaskExecutor")
fun printNumber4(x: String) {
Thread.sleep(SplittableRandom().nextLong(3000))
logger.info("[${Thread.currentThread().name}] $x")
}
}
@Configuration
@EnableAsync
class AsyncConfig {
@Bean(name = ["threadPoolTaskExecutor"])
fun threadPoolTaskExecutor(): Executor =
ThreadPoolTaskExecutor().apply {
corePoolSize = 3
maxPoolSize = 30
setQueueCapacity(10)
setThreadNamePrefix("TOMAS Executor-")
initialize()
}
}
'
4번째 방법의 장점
- 1, 3 번 방법은 Thread 의 갯수가 관리되지 않는다. 작업이 많으면 스레드가 무한정 늘어난다.
- 1, 2 번 방법은 동기/비동기 메서드로 바꿀 때 코드의 수정이 필요하다.
4번째 방법은 동기로 처리하고 싶다면 에너테이션을 빼고, 비동기면 붙이면 된다. 코드를 수정할 필요가 없다는 것이다.
그리고 ThreadPoolTaskExecutor 객체를 제공하는 방법으로 스레드의 갯수도 관리가 가능하다.
@Async 동작 방식 : 프록시
@Async 가 붙은 메서드의 클래스를 스프링은 AOP 를 이용하여 프록시 객체로 감싼다.
이 프록시 객체에서 비동기의 코드가 붙는다.
이를 RTW (RunTime Weaving) 이라 한다. Weaving 이란, Aspect 를 Method 와 엮는, 즉 프록시 객체와 실제 메서드를 엮는것을 말한다.
프록시로 만들어지기에, @Async 에서 제약사항이 생긴다.
- private 메서드는 적용되지 않음. (프록시 밖에서 접근 불가능)
- 클래스의 다른 메서드가 @Async 가 붙은 메서드를 호출하는 것은 적용되지 않음. (객체 내부에서 타는 로직은 프록시를 타지 않음)
- 런티임시 동작이 늘어나므로, 약간의 성능저하.
@Async 리턴타입
void 가 아니면 Future 로 감싸서 리턴한다.
@Async
비동기로 처리되는 해당 thread만 죽는다. 외부에서는 여러 스레드 중 어떤 스레드가 죽었는지 알 수 없다.
따라서, 로그로 남기기 위해서는 핸들러를 만들어줘야 한다.
class AsyncConfigurerConfiguration : AsyncConfigurer {
...
override fun getAsyncUncaughtExceptionHandler(): AsyncUncaughtExceptionHandler {
return AsyncUncaughtExceptionHandler { ex, method, param -> println(ex) }
}
}
@Async AOP Proxy 제약사항 해결
위에서 AOP 방식을 사용함으로서 발생하는 제약사항 3가지를 봤었다.
프록시 객체 라는 중간 매체를 사용함으로 발생하는 문제였다.
그렇다면, @Async 코드가 붙은 작업을 비동기로 처리해주는 작업을 프록시 객체를 통해 실행이 아닌,
코드를 직접 붙여넣는다면 해당 문제는 발생하지 않는다.
Spring Aop 를 Proxy 모드가 아닌 AspectJ 모드로 사용하면, 컴파일타임에 바이너리파일을 필요한 부분에 Weaving 한다.
컴파일 타임에 이를 진행하는 것을 CTW (CompileTime Weaving) 이라 한다.
참고 : http://dveamer.github.io/java/SpringAsyncAspectJ.html
https://jeong-pro.tistory.com/187
'TIL' 카테고리의 다른 글
TIL) cherry-pick 외 git 각종 취소 명령어들, ORIG_HEAD (0) | 2022.02.03 |
---|---|
TIL) 쉘 스크립트, linux screen 명령어 (0) | 2022.01.31 |
TIL) 객체의 커스텀 직렬화 (0) | 2021.11.21 |
TIL) 터미널에서 sftp 접속, Jackson2HttpMessageConverter (0) | 2021.11.20 |
TIL) @JsonFormat/@JsonSerialize, Rest response 방식의 404, configureDefaultServletHandling (0) | 2021.11.18 |