일단 새로운 프로젝트 만들고 premain 추가해주는 것 까진 강의를 그대로 따라하시면 됩니다. manifest plugin 부터 조금 차이가 있어서 거기부터 설명하면, build gradle 에 다음과 같이 추가한다. tasks.named('jar') { manifest { attributes( 'Implementation-Title': project.name, 'Implementation-Version': project.version, 'Premain-Class' : "com.java.magicianAgent.MagicianAgent", 'Can-Redefine-Classes' : true, 'Can-Retransform-Classes' : true) } } 터미널에서 다음 명령어를 통해 build 한다 ./gradlew clean build build.libs file 안에 있는 jar file 을 확인한다. (옵션) 강의에서와 마찬가지로 zip file 로 변경하면 확인가능합니다. 저같은 경우 SNAPSHOT.jar 과 SNAPSHOT -plain.jar 이렇게 2개가 생겼는데 SNAPSHOT.jar 은 제가 spring boot 로 실행서 그런지 관련 설정들이 보이고 SNAPSHOT -plain.jar 이 맞는거 같더라구요. jar file 의 절대 경로를 복사해 VM option 에 추가한다. 여기서부터는 다시 강의와 같습니다. VM option 이 안보이시면 오른쪽에 Modify options 클릭하면 add vm options 라고 보이실 겁니다. 이상한거나 궁금한거 있으시면 말씀해주세요. gradle 을 쓰시는 모든 분들도 마술을 성공시킵시다 하하
2일째 고민을 해보았는데.. 2%까지만 맞고 틀리다고 합니당..ㅠㅠ 포기하고 싶지 않은 마음에 도움을 요청합니다!! 제가 짠 코드는 이렇습니다! import java.io.*; import java.util.*; // 불이 하나가 아닌 여러개일 수 있다. public class Main { public static int R; public static int C; public static String[][] map; public static int[][] visitedFire; public static int[][] visitedHuman; public static int[] dx; public static int[] dy; public static Node fireLocation; public static Node humanLocation; public static Queue<Node> fireQueue; public static void main(String[] args) throws IOException { // 초기화 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); StringTokenizer st = new StringTokenizer(br.readLine()); R = Integer.parseInt(st.nextToken()); C = Integer.parseInt(st.nextToken()); map = new String[R][C]; // 0으로 초기화 visitedFire = new int[R][C]; visitedHuman = new int[R][C]; dx = new int[]{0,1,0,-1}; dy = new int[]{-1,0,1,0}; fireQueue = new LinkedList<>(); // 맵 입력 for (int i = 0; i < R; i++) { String S = br.readLine(); for (int j = 0; j < C; j++) { // 불 좌표, 지훈이 좌표 찾기 map[i][j] = String.valueOf(S.charAt(j)); if(map[i][j].equals("J")){ humanLocation = new Node(j,i); map[i][j] = "."; // .으로 변경 }else if(map[i][j].equals("F")){ // 불이 여러개, 불이 아무것도 없을 수 있다. -> 반레 fireLocation = new Node(j,i); fireQueue.add(fireLocation); visitedFire[i][j] = 1; } } } // (1) 불이 이동할 수 있는 최단경로 fireBfs(); // (2) 사람이 이동할 수 있는 최단경로 + 길 비교해야함 humanBfs(humanLocation.y, humanLocation.x); // 가능한 길 찾아서 int result = Integer.MAX_VALUE; // (3) 가장 짧은 최단거리 찾기 (가장자리 찾기) for (int i = 0; i < visitedHuman.length; i++) { for (int j = 0; j < visitedHuman[i].length; j++) { if((0<i && i<R-1) && 0 < j && j < C-1)continue; // 가장자리가 아닌 경우 pass if(visitedHuman[i][j] > 0) { result = Math.min(result, visitedHuman[i][j]); } } } if(result == Integer.MAX_VALUE){ System.out.println("IMPOSSIBLE"); }else{ System.out.println(result); } } public static void humanBfs(int y, int x){ // tkfk visitedHuman[y][x] = 1; Node node = new Node(x,y); Queue<Node> queue = new LinkedList<>(); queue.add(node); while(queue.size()>0){ Node cur = queue.poll(); for (int i = 0; i < 4; i++) { int nx = cur.x + dx[i]; int ny = cur.y + dy[i]; if(nx < 0 || nx >= C || ny < 0 || ny >= R) continue; if(visitedHuman[ny][nx] == 0 && (map[ny][nx].equals("."))){ if(visitedHuman[ny][nx] < visitedFire[ny][nx] || visitedFire[ny][nx] == 0){ visitedHuman[ny][nx] = visitedHuman[cur.y][cur.x] + 1; Node next = new Node(nx,ny); queue.add(next); } } } } } public static void fireBfs(){ while(fireQueue.size()>0){ Node cur = fireQueue.poll(); for (int i = 0; i < 4; i++) { // 4방향 탐지 int nx = cur.x + dx[i]; int ny = cur.y + dy[i]; if(nx < 0 || nx >= C || ny < 0 || ny >= R) continue; if(visitedFire[ny][nx] == 0 && (map[ny][nx].equals("."))){ visitedFire[ny][nx] = visitedFire[cur.y][cur.x] + 1; // 이동함을 표현 Node next = new Node(nx,ny); fireQueue.add(next); } } } } public static class Node { int x; int y; Node(int x, int y){ this.x = x; this.y = y; } } }
안녕하세요 강의를 듣다가 궁금증이 생겨서 질문을 드립니다. void init() { // given List<Product> products = List.of( createProduct("001", 1000), createProduct("002", 2000) ); // when Order order = Order.create(products, LocalDateTime.now()); // then assertThat(order.getOrderStatus()).isEqualByComparingTo(OrderStatus.INIT); } 강의에서 OrderTest 클래스 테스트 과정에서 enum을 비교할때 isEqualByComparingTo를 사용하셨습니다. assertThat(order.getOrderStatus()).isEqualTo(OrderStatus.INIT); 위와 같이 isEqualTo를 사용해도 enum을 비교할 수 있는데, 혹시 isEqualTo 대신 isEqualByComparingTo를 사용한 이유를 알 수 있을까요? isEqualByComparing가 정의된 AbstractComparableAssert.java를 확인해보면 BigDecimal을 예시로 들어서 사용법을 설명하고 있습니다. BigDecimal의 예시로 Enum을 써야 하는 방법을 생각해보았는데, 유추(?)가 잘 되지 않아서 질문을 남깁니다. 항상 좋은 강의 제공해주셔서 감사합니다!!😀😀
안녕하세요, 사내에서 채팅 프로젝트를 진행하게 되어 강의를 신청해 수강 중에 있습니다. ( 채팅은 대규모 서비스를 전제로 하고 있지만, 현재는 상담사와 고객의 1:1 상담을 먼저 진행할 예정입니다. ) 관련해서 두가지 질문이 있습니다! (1) 채팅 서버 통신 구조 설계: 팀원들과 함께 이야기를 나눠볼 때, Socket으로 채팅 히스토리를 조회하거나 채팅 내용을 저장할 수 있지 않을까? 라는 이야기가 나왔습니다. 강의에서 채팅 서버 통신 구조를 설계할 때, 제로초님께서는 HTTP와 Socket을 혼합해서 사용하고 계시는데요. HTTP와 Socket을 함께 사용하시는 이유가 있는지 궁금합니다. 또한, 대규모 서비스로 전환 될 것을 전제하며 실제 서비스를 설계할 때는 어떤 방식이 더 나은지 궁금합니다. (2) DB 관리 : 채팅 히스토리를 관리하는 방법을 두가지로 고민중에 있습니다. 저장 DB는 mongoDB를 생각 중에 있습니다. (A. 인메모리에 저장해두었다가 주기적으로 DB에 저장 B. 건마다 DB에 저장) 개인 적으로는 A방법을 진행했을 때, 데이터 유실이 우려되어 B방법이 더 나은 옵션이지 않을까 생각하지만, 빈번한 DB접근이 우려됩니다. 제로초님은 어떤 방식으로 관리하고 계신지 조언주시면 감사하겠습니다!
강의 내용과 거리가 먼 질문이지만 제가 채팅 프로젝트를 만드는데 현재 ec2에서 docker로 배포를 하면서 진행 중인데 배포시 힙 메모리 부족으로 서버가 열리지 않아 기존 t2.micro에서 t3.small로 인스턴스 유형을 변경하니 서버가 정상적으로 실행이 됩니다. 다음은 스왑을 적용한 t2.micro와 t3.small 인스턴스에서의 램 상황입니다. 보시다시피 기존 t2.micro일 때 는 swap메모리의 크기와 상관없이 거의 사용하지 않는데 swap메모리는 hdd에서 끌어다 쓰다보니 swap메모리를 사용하는데 제한이 있는건가요?? 다음은 힙메모리 초과로 서버가 열리지 않을 때 입니다. <--- Last few GCs ---> [18:0x7f6622d90300] 21689 ms: Scavenge (reduce) 481.9 (490.6) -> 481.5 (491.1) MB, 15.1 / 0.0 ms (average mu = 0.143, current mu = 0.005) allocation failure; [18:0x7f6622d90300] 22102 ms: Mark-sweep (reduce) 482.2 (491.1) -> 481.9 (491.9) MB, 349.1 / 0.0 ms (+ 121.4 ms in 21 steps since start of marking, biggest step 21.3 ms, walltime since start of marking 503 ms) (average mu = 0.231, current mu = 0.312) <--- JS stacktrace ---> FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory npm ERR! path /home/app npm ERR! command failed npm ERR! signal SIGABRT npm ERR! command sh -c nest start --watch npm ERR! A complete log of this run can be found in: npm ERR! /root/.npm/_logs/2023-07-07T09_03_12_827Z-debug-0.log
안녕하세요 드디어 강의 완강했습니다!!^^ 뒷부분 부터는 퀴즈가 없어서 비교적 빨리 수강했네요. 강의 후에 해봐야 할 것들을 생각해보았는데 조언을 듣고 싶어서 질문남깁니다. 일단은 지금 것 했던 "나만의 ~~프로젝트"를 리팩토링(성능/안정성/가독성/로직개선)하고 api기능추가, 테스트코드 작성, DB쿼리성능개선등을 해보고자합니다. 전부 다 하려면 시간이 오래걸리겠지만(ㅠ) 마지막에 취업준비강의에서 말씀하신 3년차개발자로 생각되기 위해선 해야할게 많은 것 같습니다..ㅎㅎ(3년차 같은 신입을 뽑는다니!!ㅠㅠ) 그래서 일단 목표는 실제 현업에서 하는 것처럼 코드를 작성해보고자 하는데요, 막상 하려니 좀 막막하네요. 질문은: 지금 제 생각은 현업에서 쓰는 좋은 코드를 보고 어떤 식으로 설계했는지 테스트코드는 어떻게 작성했는지 등등 참고하고 분석하고 공부해서 제 나름대로 프로젝트를 리팩토링해보고 싶은 생각입니다. 그게 가장 실력도 늘 것 같구요. 그래서 혹시 관련 코드나 책이나 자료등이 있으면 추천해주시면 감사하겠습니다. 아! 그리고 백엔드 심화강의에서 마이크로큐와 await의 관계를 굉장히 감명깊게 들었습니다. 비동기과정이 정말 헷갈렸는데 속이 시원해졌습니다.ㅎㅎ 혹시 이런 자바스크립트 원리나 cs관련내용도 추천해주실만한 책이나 자료 있으면 알려주시면 감사하겠습니다. 일단 방향은 이렇게 잡았는데 조언해주시면 참고하겠습니다!! 끝으로 제가 지금것 들었던 개발강의중 가장 자세하고 친절하고 이해도 잘되고 재밌는 강의였습니다!! 퀴즈 할때나 버그나 에러날때 힘들긴 했지만, 그래도 개발이 점점 더 재밌어지고 더 잘하고 싶네요. 궁금한 것있으면 또 질문해도 되겠죠?^^;;; 감사합니다!!
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요. 1. 강의 내용과 관련된 질문을 남겨주세요. 2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요. (자주 하는 질문 링크: https://bit.ly/3fX6ygx) 3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요. (질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG) 질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요. ========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예/아니오) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) [질문 내용] 강의를 듣다 의문점이 생겨 질문 드립니다. OrderDto는 JPA에서 생성된 객체가 아니고 직접 생성자로 만들어서 사용한 객체인데 왜 Hibernate5Module이 프록시 객체로 생각해서 null값을 반환하는지 모르겠습니다. 제가 긴시간 찾아보면서 생각한 이유는 이렇습니다. 강의 5분경 쯤에 api 호출시 Json에서 orderItems 가 null로 반환되는것을 보여주십니다. orderItems가 null로 반환되는것은 스프링이 string으로 응답을 보낼때 messageConverter가 json으로 변환해서 반환을합니다. 이때 orderItems가 JPA(Hibernate)에서 만든 프록시 객체로 되어있어 전강의에서 설정한 Hibernate5Module이 프록시 객체를 null로 바꾸기 때문에 orderItems가 null로 나타낸것으로 생각됩니다. 하지만 제가 디버깅 모드로 확인해본 결과 OrderDto안에 있는 orderItems는 프록시 객체가 아닌 실제로 생성된 객체였습니다.(OrderDto는 단순히 JPA가 관리 하지 않아서 당연하다고 생각합니다.) 강의 기준으로 orderItems 안에 있는 orderItem객체가 2개 존재하는데 orderItem객체는 JPA를 통해 생성된 객체이고 자세히 살펴보면 orderItem 안에 Item은 LAZY로딩으로 프록시 객체가 생성되어있습니다. 혹시몰라 Hibernate5Module을 끄고 실행한 결과 Item이 @JsonIgnore가 있으면 orderItems가 제대로 결과 값이 나오고 @JsonIgnore이 없으면 프록시 객체를 Json으로 변환하지 못해서 오류가 발생합니다. 그럼 Hibernate5Module는 엔티티 안에 객체의 값중 하나라도 프록시 객체가 있다면 다른 객체의 값이 있든 말든 그냥 null을 반환하는 걸까요?? 제가 이해한것이 맞는지 궁금해서 질문드립니다. 긴글 읽어주셔서 감사합니다.
[질문 내용] 상품주문_재고수량초과 테스트할 때 Junit5 사용해서 expected가 없습니다. import static org.junit.jupiter.api.Assertions.*; @Test public void 상품주문_재고수량초과() throws Exception { //given Member member = createMember(); Item item = createBook("시골", 10000, 10); int orderCount = 11; //when //then assertThrows(NotEnoughStockException.class, ()-> orderService.order(member.getId(), item.getId(), orderCount)); } 그래서 이렇게 작성해서 초록불은 떴는데 적절하게 작성했는지 궁금합니다. 더 좋은 예시가 있는 지 궁금합니다.