학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요. 1. 강의 내용과 관련된 질문을 남겨주세요. 2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요. (자주 하는 질문 링크: https://bit.ly/3fX6ygx) 3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요. (질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG) 질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요. ========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예/아니오) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) [질문 내용] 여기에 질문 내용을 남겨주세요.
저는 컴터 사양이 꽤나 좋아서 인지? 항상 표준출력이 더 빠르더군요. 알아보니 로깅 프레임워크가 느린 이유에는 대략 이런 이유들이 있네요. 로그 출력에 여러 과정을 거침. Logger → Appender → Encoder → Layout → OutputStream 레벨 설정에 의한 필터링 로그 포맷팅 처리 분명 표준출력이 synchronized 에 의해서 멀티 스레드 환경에서 취약한건 맞으나 현재 테스트에서는 그 부분이 로깅 프레임워크의 처리 과정보다는 빨랐던 것 같습니다. 테스트 환경 변경 그래서 테스트 환경을 바꿔서 테스트 해보았습니다. 멀티스레드 환경. 스레드 10개. 각 스레드별 100000번 출력 package kr.co.shortenurlservice.log; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @Slf4j public class SoutVsAsyncLoggingTest { private static final int THREAD_COUNT = 10; private static final int LOG_COUNT = 100_000; public static void main(String[] args) throws InterruptedException { // 1. System.out.println() 테스트 long sysoutTime = runTest(i -> System.out.println("[SYSOUT][" + Thread.currentThread().getName() + "] " + i) ); // 2. log.info() (AsyncAppender + Console) 테스트 long asyncLogTime = runTest(i -> log.info("[LOGGER][{}] {}", Thread.currentThread().getName(), i) ); // 결과 출력 Thread.sleep(5000); System.out.printf("System.out: %,d ms%n", sysoutTime); System.out.printf("Async log : %,d ms%n", asyncLogTime); } private static long runTest(StringConsumer task) throws InterruptedException { long start = System.currentTimeMillis(); ExecutorService executor = Executors.newFixedThreadPool(THREAD_COUNT); CountDownLatch latch = new CountDownLatch(THREAD_COUNT); for (int t = 0; t < THREAD_COUNT; t++) { executor.submit(() -> { for (int i = 0; i < LOG_COUNT; i++) { task.accept(i); } latch.countDown(); }); } latch.await(); executor.shutdown(); long end = System.currentTimeMillis(); return end - start; } @FunctionalInterface interface StringConsumer { void accept(int value); } } 또한 test/resources/logback.xml 설정으로 포맷을 날짜 같은것 제외하고 표준출력과 똑같이 맞춰주고 AsyncAppender 를 통해 멀티 스레드 환경에서 차별점이 있도록 비동기 Appender를 추가해줌. <configuration> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%msg%n</pattern> </encoder> </appender> <appender name="ASYNC_CONSOLE" class="ch.qos.logback.classic.AsyncAppender"> <appender-ref ref="CONSOLE"/> <queueSize>10240</queueSize> </appender> <root level="info"> <appender-ref ref="ASYNC_CONSOLE"/> </root> </configuration> 이렇게 하니까 표준출력보다 빠르더군요. 꼭 성능문제뿐 아니라 로그 관리체계 자체가 로깅 프레임워크가 넘사이기 때문에 안쓸 비교불가겠지만.. 성능적으로 보더라도 실무 환경이라면 메시지 출력 전후로 비즈니스 로직들이 있기 때문에 임계영역을 통해 스레드를 대시시키는 표준출력을 사용하는것보다는 로깅 프레임워크가 더욱 성능적으로 이점이 있을 것 같습니다.
안녕하세요! 접근제어자 강의 중 BankAccount를 클래스를 만들어 활용하는 내용입니다. 입금 & 출금 메서드를 만들고 있고, -100원이나 0원처럼 부적절한 금액이 입금되지 않도록 private boolean isAmountValid를 활용하여 amount>0인지 확인하는 중입니다. 이해가 잘 안 가는 부분이: isAmountValid는 반환값이 boolean이니 return amount > 0이면 true가 반환되는 것인가요? if(true)이면 그냥 무한반복 아닌가요? 만약에 amount가 0보다 작으면 boolean이 false로 반환되나요, 아니면 아예 return amount > 0 에 해당되지 않아서 값을 반환하지 않는 건가요? boolean이 너무 헷갈려서 질문을 잘 한 것인지 조차 모르겠네요.. 헷갈리신다면 죄송합니다. package access; public class BankAccount { private int balance; public BankAccount() { //어차피 초기값 0이어서 없어도 되는데 public 사용해보려고 balance = 0; } //public 메서드: deposit public void deposit(int amount) { if (isAmountValid(amount)) { //isAmoutnValid(amount) = true 라고 왜 표기를 안 해도 되지? balance += amount; } else { System.out.println("유효하지 않은 금액입니다."); } } private boolean isAmountValid(int amount) { //정상적인 금액 입력됐는지 검증하는 로직. 내부에서만 쓸 거임. //금액이 0보다 커야함 return amount > 0; //반환값이 boolean이므로 true/false가 반환됨. } }
우선 강사님이 말씀하신 강의 내용에서 백엔드 서버에 UI를 생성하고 배치하는데 도움을 주는 API를 설계하는 부분에서 깊은 영감을 받았습니다! 또한 서버비용을 낮추기 위한 서버 구축 방법에도 많이 도움을 받았습니다. 그런데 강의에서 말씀하신 방법으로 API 구현 및 서버구축에 대한 고민을 하면서 든 생각이 앱1개당 필요로 하는 API가 최소 몇십개는 될거라고 예상을 했습니다. 그러다 보니 필연적으로 서버도 많이 질수 밖에 없지 않나라는 생각이 들었습니다. 여기서 질문을 하나 드리고 싶습니다. 그러면 혹시 서버에 구현하신 API들은 강의때 말씀하셨던 UI 자동 생성 및 관리를 위한 API와 푸시알림, 공지를 위한 알림창 등과 같이 300개의 앱이 공통적으로 필요로 하는 API들만 구현을 하시는건건지 궁금합니다. 또한 앱마다 각자가 표시하고자 하는 고유한 데이터(예를 들면 날씨앱이면 날씨 관련데이터)의 CRUD와 관련된 부분은 API로 개발하는게 아닌 앱안에서 모든것을 처리를 하는 방식인건지 궁금합니다~
안녕하세요 우선 저는 gradle 로 진행을 했습니다. 다른건 다 동일하게 했고 젠킨스 도커 안에 들어가서 cd /var/jenkins_home/workspace/My-Third-Proejct nohup java -jar build/libs/jenkins_cicd_project-0.0.1-SNAPSHOT.jar --server.port=9000 --server.address=0.0.0.0 > app.log 2>&1 & 로 실행을 하면 브라우저에서 localhost:9000 으로 접근이 잘 됩니다. 그런데 젠킨스 스크립트로 실행을 하게 되면 젠킨스 도커 내의 로그를 확인해보면 계속 꺼져요. 이유가 있을까요? 젠킨스 구성 설정의 shell script 는 아래와 같아요. # 기존 프로세스 종료 PID=$(ps -ef | grep "jenkins_cicd_project-0.0.1-SNAPSHOT.jar" | grep -v grep | awk '{print $2}') if [ ! -z "$PID" ]; then echo "종료 중인 애플리케이션 PID: $PID" kill -15 $PID sleep 5 fi # 새 프로세스 시작 (setsid 사용하여 별도 세션에서 실행) echo "애플리케이션 시작 중..." setsid nohup java -jar build/libs/jenkins_cicd_project-0.0.1-SNAPSHOT.jar --server.port=9000 --server.address=0.0.0.0 > app.log 2>&1 & APP_PID=$! echo "애플리케이션 PID: $APP_PID" # 시작 확인 sleep 10 ps -p $APP_PID > /dev/null if [ $? -eq 0 ]; then echo "애플리케이션이 PID $APP_PID로 실행 중입니다." curl -s http://localhost:9000 > /dev/null if [ $? -eq 0 ]; then echo "애플리케이션에 정상적으로 접근할 수 있습니다." else echo "애플리케이션 접근 테스트 실패!" fi else echo "애플리케이션 시작 실패!" exit 1 fi 그리고 젠킨스 빌드 console out 도 아래와 같습니다.Started by user 허강준 Running as SYSTEM Building in workspace /var/jenkins_home/workspace/My-Third-Proejct The recommended git tool is: NONE No credentials specified > git rev-parse --resolve-git-dir /var/jenkins_home/workspace/My-Third-Proejct/.git # timeout=10 Fetching changes from the remote Git repository > git config remote.origin.url https://github.com/Highjune/jenkins_cicde_project.git # timeout=10 Fetching upstream changes from https://github.com/Highjune/jenkins_cicde_project.git > git --version # timeout=10 > git --version # 'git version 2.39.5' > git fetch --tags --force --progress -- https://github.com/Highjune/jenkins_cicde_project.git +refs/heads/*:refs/remotes/origin/* # timeout=10 > git rev-parse refs/remotes/origin/main^{commit} # timeout=10 Checking out Revision 6353899ff277302005bce6da4d498211ebeab06b (refs/remotes/origin/main) > git config core.sparsecheckout # timeout=10 > git checkout -f 6353899ff277302005bce6da4d498211ebeab06b # timeout=10 Commit message: "포트번호 수정" > git rev-list --no-walk 6353899ff277302005bce6da4d498211ebeab06b # timeout=10 [My-Third-Proejct] $ /bin/sh -xe /tmp/jenkins9188074144619929309.sh + ps -ef + grep jenkins_cicd_project-0.0.1-SNAPSHOT.jar + grep -v grep + awk {print $2} + PID=283 + [ ! -z 283 ] + echo 종료 중인 애플리케이션 PID: 283 종료 중인 애플리케이션 PID: 283 + kill -15 283 + sleep 5 + echo 애플리케이션 시작 중... 애플리케이션 시작 중... + APP_PID=420 + echo 애플리케이션 PID: 420 애플리케이션 PID: 420 + sleep 10 + setsid nohup java -jar build/libs/jenkins_cicd_project-0.0.1-SNAPSHOT.jar --server.port=9000 --server.address=0.0.0.0 + ps -p 420 + [ 0 -eq 0 ] + echo 애플리케이션이 PID 420로 실행 중입니다. 애플리케이션이 PID 420로 실행 중입니다. + curl -s http://localhost:9000 + [ 0 -eq 0 ] + echo 애플리케이션에 정상적으로 접근할 수 있습니다. 애플리케이션에 정상적으로 접근할 수 있습니다. Finished: SUCCESS 그런데 계속 Graceful shutdown complete 가 뜹니다. 참고로 다른 포트랑 충돌같은 것은 없습니다 원래 8081로 진행하다가 일부러 다른 포트로 시도해보려고 9000으로 했습니다. 이렇게 젠킨스 내 컨테이너 내에서 수동 실행하면 되는데 왜 젠킨스 빌드로 하면 꺼지는 걸까요? 참고로 젠킨스 도커 실행 명령어는 아래와 같습니다. docker run -d -v jenkins_home:/var/jenkins_home -p 8080:8080 -p 9000:9000 -p 50000:50000 --restart=on-failure --name jenkins-server jenkins/jenkins:lts-jdk21
DIP 예제 코드에서 질문이 있습니다. Switchable 이라는 interface를 객체로 Switch가 갖게되는데요. 구조가 이렇게 바뀌어도 Fan의 turnOn 함수에 예를 들어 매개변수가 들어가게 되면 (Overloading) Switch 클래스에도 영향이 가지 않나요? switch class의 tunrOn함수의 device.turnOn() 함수도 바뀌어야 하니까요..?
안녕하세요. 10:33초 쯤에 BP_AB_CharacterPlayer 로 작업하시는데 제 프로젝트에는 그런 클래스가 보이지 않네요 제가 뭘 놓치면서 진행하고있었나요? 뿐만아니라 BP_ABGameMode, ABPlayer_Controller이런것들이 안보입니다. 파일 탐색기에서 봤을땐 Controller, GameMode,Congratulation이런 파일들이 있는데 에디터에선 저렇게 3개 밖에 보이지않네요... 물론 BP_AB_CharacterPlayer는 파일에도 없습니ㅏㄷ.
안녕하세요 강사님, 현재 강의를 참고하여 팀원끼리 클론 코딩 프로젝트를 하기위해 EC2에서 Spring Boot + MySQL 서비스를 Docker Compose로 구성하여 배포 중입니다. GitHub Actions를 통해 ECR에 푸시한 Spring Boot 이미지를 EC2에서 pull 받아서 docker-compose로 실행하는 파이프라인을 구축했습니다. 현재까지 진행한 작업에 대해 말씀드리겠습니다. Spring Boot 애플리케이션을 Dockerfile로 빌드해 Amazon ECR에 업로드 EC2에 MySQL과 Spring Boot를 각각 컨테이너로 구성 ( docker-compose.yml 사용) GitHub Actions에서 EC2로 SSH 접속 후 .env 생성 및 docker-compose up -d 자동 실행 설정 현재 EC2 인스턴스(t2.micro)에 연결이 실패하고있습니다. ssh -v -i를 통해 로그를 확인 해본 결과 연결은 성공하지만 인증 전 또는 핸드쉐이크 중에 connection reset 이 발생하였다고 합니다. 현재 인스턴스 유형은 t2.micro이며, Docker로 2개 컨테이너가 실행 중이었습니다. CPU/RAM 자원이 부족해 SSH 연결도 거부되는 것 아닌가 의심됩니다. 혹시 이 경우 인스턴스 유형을 업그레이드를 하는게 해결방법일지 혹은 다른 방법이 있을까요?
[질문 내용] 안녕하세요. 강의 14분쯤에, @Test 부분에서 .isInstanceOf(SQLException.class)를 했을 때, 테스트가 성공하는 이유가 궁금합니다. 저는 ConnectException과 SQLException이 서로 관련이 없는 Exception이기 때문에 테스트가 실패해야 한다고 생각했습니다. 혹시 Service의 logic()에서 repository.call()이 호출 될 때 SQLException이 터지고, 그로 인해서 networkClient.call()은 실행되지 않고 끝나기 때문에 테스트가 성공하는건가요?