현재 스프링부트에서 게시글을 등록하는 기능을 만들고 detail 페이지에서 등록한 내용을 확인할 수 있도록 설정했습니다. 게시글 제목, 내용, mp4 영상을 받도록 하고 있는데 게시글 등록 후 바로 detail 페이지에 들어가 확인하면 동영상이 보이지 않습니다. 그래서 경로가 잘못됐나 싶어서 페이지 소스 보기를 통해 확인해봤는데 경로도 정확하게 호출되고 있습니다. 그 후 서버 재실행 후 다시 확인해보니 그제서야 동영상이 보이고 있습니다. 이에 대한 해결 방법을 잘 모르겠습니다. 아래는 게시글 등록에 대한 컨트롤러 코드, 서비스 코드, html 코드입니다.
[질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예/아니오) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) [질문 내용] 여기에 질문 내용을 남겨주세요. input.nextLine(); 이거말고 처음에 Scanner scanner = new Scanner( System.in ) 이걸 우연히 while 안에 넣어서 해도 되는걸 확인했는데 이런 방법은 좋은 방법이 아닌가요?
좋은 강의 덕분에 공부를 열심히 잘 하고 있습니다 선생님 감사드립니다. 다름이아니라 이번 강의 중 cropper 사용에서 똑같이 설치 후 이미지 파일을 불러오면 Uncaught TypeError: $newImage.cropper is not a function at reader.onload 지정된 함수가 아니라고 뜨는데 cropper가 안 먹혀서 그런 걸까요 ㅠㅠ..? 서치하고 코드를 계속 봐도 답이 나오지 않아 이렇게 질문 드립니다..
자바와 스프링 부트로 생애 최초 서버 만들기, 누구나 쉽게 개발부터 배포까지! [서버 개발 올인원 패키지]
안녕하세요 강사님!! 다름이 아니라 공부를 하다가 MVC와 API의 차이점(?)에 대해 알아보게 되었습니다! 그래서 내린 결론이 Spring MVC구조의 @Controller는 컨트롤러의 리턴값이 ViewResolver에 의해 해석되어서 특정 View가 html응답으로 전송된다. 반면 REST API구조의 @RestController에 의한 호출은 View를 리턴하는 것이 목적이 아닌, 데이터를 전송하는 것을 목적으로 응답이 전송되어 진다. 라고 내렸습니다. 첫번째로, 위에서 제가 내린 결론이 맞는 말인지와 두번째로, 그럼 MVC를 쓰는 경우와 API를 쓰는 경우가 언제인지가 궁금합니다. 제가 아직 개념이 부족해서 그런건지는 몰라도 MVC를 쓰면 프론트엔드의 역할이 없어지는게(?) 아닌가 하는 착각이 들어서... 답변 부탁드리겠습니다!!
import java.util.*; public class Main { public static void main(String[] args){ Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int[][] arr = new int[n][n]; for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { arr[i][j] = sc.nextInt(); } } int m = sc.nextInt(); int[] moves = new int[m]; for(int i = 0; i < m; i++){ moves[i] = sc.nextInt(); } ArrayList<Stack<Integer>> board = new ArrayList<>(); for(int i = 0; i < n; i++) { Stack<Integer> temp = new Stack<>(); for(int j = 0; j < n; j++) { if(arr[n-1-j][i] == 0) break; temp.push(arr[n-1-j][i]); } board.add(temp); } Stack<Integer> basket = new Stack<>(); int result = 0; for(int i = 0; i < m; i++) { int num = moves[i]-1; Stack<Integer> stack = board.get(num); if(stack.empty()) continue; int popNum = stack.pop(); if(!basket.isEmpty() && basket.peek() == popNum) { basket.pop(); result += 2; } else { basket.push(popNum); } } System.out.print(result); } } 어느 부분에서 틀린지 모르겠습니다. 확인 해주시면 감사하겠습니다.ㅠㅠ
[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part9: MMO 컨텐츠 구현 (DB연동 + 대형 구조 + 라이브 준비)
안녕하세요? 루키즈님 덕분에 서버에 대해 많은 것을 배우고 저만의 프로젝트도 개발하고 있는 개발자 입니다. (항상 감사하게 생각하고 있습니다.) 질문은 3~4일 간격으로 CPU가 99% 사용되는 문제가 발생하여 원인 분석 차 CPU가 99% 사용되는 현상이 발현된 시점에 visual studio 성능 프로파일러를 통한 결과를 얻었습니다. 헌데 ServerCore.Listner.OnAcceptCompleted에서 많은 양의 CPU를 사용하고 있는 것으로 파악되는데 실제로 Connect요청은 없었습니다. 심지어 DummyClient를 이용해 1000명의 플레이어가 기본플레이 까지 했을때도 발생안하다가, 3~4일 후 아무도 접속안하고있는 시점에 발생합니다. 아래 RecvBuffer 코드와 diagsession 정보를 보시고 답변 부탁드립니다. 혹여 정보가 부족하거나 추가로 확인 필요한 부분 말씀해주시면 다시 확인해서 질문드리겠습니다. 감사합니다. diagsession 캡쳐화면 RecvBuffer 코드 buffer size : 65535 public class RecvBuffer { ArraySegment<byte> _buffer; int _readPos; int _writePos; public RecvBuffer(int bufferSize) { _buffer = new ArraySegment<byte>(new byte[bufferSize], 0, bufferSize); } // [r] [] [] [w] [] [] [] [] [] [] [] 일때 // <Data>[r] [] []</Data> <Free>[w] [] [] [] [] [] [] []</Free> // read의 위치 public int DataOffset => _buffer.Offset + _readPos; // read ~ write까지의 사이즈 public int DataSize => _writePos - _readPos; // write의 위치 public int FreeOffset => _buffer.Offset + _writePos; // wirte ~ buffer의 마지막 public int FreeSize => _buffer.Count - _writePos; public ArraySegment<byte> ReadSegment => new ArraySegment<byte>(_buffer.Array, DataOffset, DataSize); public ArraySegment<byte> WriteSegment => new ArraySegment<byte>(_buffer.Array, FreeOffset, FreeSize); public void Clean() { int dataSize = DataSize; if (dataSize == 0) { // 남은 데이터가 없으면 커서 위치만 리셋 _readPos = _writePos = 0; return; } // 남은 데이터가 있으면 시작위치로 복사 Array.Copy(_buffer.Array, DataOffset, _buffer.Array, _buffer.Offset, dataSize); _readPos = 0; _writePos = dataSize; } public bool OnRead(int numOfBytes) { if (numOfBytes > DataSize) return false; _readPos += numOfBytes; return true; } public bool OnWrite(int numOfBytes) { if(numOfBytes > FreeSize) return false; _writePos += numOfBytes; return true; } }
현재 프로젝트를 진행중인데, Access Token과 Refresh Token을 헤더에 담아 로컬스토리지에 저장하는 방법을 사용중입니다. 그런데 로컬 스토리지는 보안에 취약하다는 글을 보았습니다. 로컬 스토리지에 담아도 jwt 정책으로 대응이 가능하다는 글도 있었습니다. Access Token과 Refresh Token을 어디에 저장해야 할까요? 만약 로컬 스토리지나 쿠키에 저장한다면 어떻게 관리해야하나요?
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요. 1. 강의 내용과 관련된 질문을 남겨주세요. 2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요. (자주 하는 질문 링크: https://bit.ly/3fX6ygx) 3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요. (질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG) 질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요. ========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예/아니오) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) [질문 내용] 여기에 질문 내용을 남겨주세요. 안녕하세요? 프로젝션 쿼리에 new 명령어 관련 헷갈리는 부분이 있어서 질문 드립니다! try{ Member member = new Member(); member.setUsername("member1"); member.setAge(10); em.persist(member); em.flush(); em.clear(); List<MemberDTO> result = em.createQuery("SELECT new jpql.MemberDTO(m.username, m.age) FROM Member m", MemberDTO.class) .getResultList(); MemberDTO memberDTO = result.get(0); System.out.println("memberDTO = " + memberDTO.getAge()); System.out.println("memberDTO = " + memberDTO.getUsername()); tx.commit(); } 위와 같은 쿼리를 실행하게 되면 상위에서 생성한 member 객체에 있는 값을 result 로 복사하고 result 값을 memberDTO List의 인덱스 0으로 들어 간 값을 조회하라는 뜻이 맞는건가요..? 수업듣다가 제가 이해한게 맞는지 헷갈려서 질문 드립니다.. 답변 부탁드립니다!! 감사합니다.
안녕하세요! 강의 너무 잘 듣고 있는 수강생입니다. 이전 동기화와 CPU의 관계 강의에서는 CPU가 두 개 이상의 명령어를 처리하면 원자성이 보장이 안된다고 설명을 하셨는데, SpinLock & Busy Waiting 강의에서 test_and_set(int *lock) 메서드는 CPU 하드웨어 계층에서 원자성이 보장된다는 게 이해가 잘 가지를 않습니다. test_and_set(int *lock) 이 메서드는 하나의 명령어만 처리하는 건가요??
[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part9: MMO 컨텐츠 구현 (DB연동 + 대형 구조 + 라이브 준비)
안녕하세요. 강의 다 듣고 포트폴리오 프로젝트를 만들면서 JobSerializer 관련해 궁금증이 생겨 질문 드립니다. 강의에서 채택한 방식은 GameRoom 단위로 JobSerializer를 두는 방식으로 이해했습니다. 그렇다면 Zone은 비대해진 GameRoom의 커버영역을 논리적으로 잘게잘게 쪼개서 최적화 하는 것이라고 보면 될까요? (만약, 좁은 영역을 다루는 보스방 같은 곳은 BossRoom 같이 새로 만들어서 Zone 없이 구현하면 되는건가요?) GameRoom을 여러 개로 나누는 시점은 독립적인 공간 (이를테면 보스방, 건물내부 등)이 생길 때 인가요? 강의에서는 GameLogicTask에서 모든 GameRoom에 대한 Flush를 하였는데, 메인월드(대륙)처럼 처리해야 하는 영역이 방대한 GameRoom에 대해서는 Thread 1개 전체를 할당해 주는 등 각 GameRoom의 무게(?) 에 따라서 Thread 1개당 몇개의 GameRoom을 Flush 할 것인지 정하면 되는 것인가요? 만약, Object 단위로 JobSerializer를 둔다면 Thread 하나당 담당할 Object 개수를 정해서 하는 건가요? 여담으로 예전에 면접에서 동접자수에 관련된 질문을 받았는데.. CPU 성능에 따라 천차만별이라 크게 의미 있는 질문인가 싶지만 이런 지표를 뽑아내려면 정확히 어떤 기준으로 뽑아내는 건가요? (대충 눈으로 끊김 없는 한계 동접자 수, CPU 로드율 일정% 이상 등등..) 감사합니다!
안녕하세요 OnRecvPacket 함수 내부에서 HandlePacket 함수의 파라미터로 PacketSessionRef 타입 파라미터를 넘겨줄 때 왜 PacketSessionRef의 생성자를 호출해서 추가로 만든 객체 를 넘겨주는지 궁금합니다. 이 부분이 혹시 shared_from_this를 활용해서 PacketSession으로 캐스팅해서 객체 본인을 넘겨줘야 하는게 아닌지 궁금합니다.
예전에 영한님이 답변하신 내용 중에 스프링이 제공하는 트랜잭션은 처음 시작한 트랜잭션을 기반으로 전파됩니다. 그러니까 서비스에서 트랜잭션을 시작하면 리포지토리의 트랜잭션이 해당 트랜잭션을 그대로 이어 받습니다. 라는 내용이 있었습니다. 그렇다면, 컨트롤러, 서비스, 리포지토리 순으로 코드가 진행된다고 할 때, 만약 서비스 계층에 @Trasnactional(readonly=false) 가 걸려있고 리포지토리 계층에 @Trasnactional(readonly=true) 가 걸려있으면 서비스 계층이 먼저 호출되므로 readonly=false 옵션이 적용된 채로 리포지토리 계층까지(기존 리포지토리 계층의 옵션은 무시된채로) 코드가 흘러가는걸까요?
제가 경험했던 환경에서는 네트워크스레드(N) -> MessageQueue -> 메인스레드(1) 사실상 메인로직이 싱글 스레드였습니다. 그렇다 보니 Queue에 들어오는 순서대로 메인스레드에서 처리했고, 느릴수도 있었겠지만 순서는 확실하게 보장이 되었습니다 그런데 현재 구조상 네트워크스레드(N) -> JobQ 메인로직(N)으로 진행되거나 네트워크스레드(N, timeLimit제한) -> GlobalQ 메인로직 (N) 으로 진행되다보니 궁금한게 생겼는데요 첫번째 궁금한점은 패킷이 ABC 순서로 왔을때, AC는 JobQ1 B는 JobQ2을 수행한다고 가정하고 A, B, C를 각각 네트워크 스레드 1,2,3가 받았다면 스레드1가 A(수행시간1초)를 처리하는 스레드2는 B(수행시간3초)를 처리하고, 스레드3은 C(수행시간1초)를 Push만 한다고 하였을때 B가 먼저 들어왔지만 처리시간이 늦어 C패킷보다 답장을 늦게 보내는 상황이 발생할것 같은데요, (시간이 1초 3초까진 아니겠지만 컴퓨터 입장에서는 충분히 코드의 짧은차이로도 발생 할 수 있는 경우라고 가정했을때) 과정에서 B처리중 죽어있으면 클리어가 안된다거나 하는 조건이 있고, B가 먼저 수행 되었지만 C처리가 먼저 끝나버려서 B의 결과가 바뀌는 경우는 어떻게 처리를 하면 좋을지 궁금합니다. (꼭 이런 1개의 처리속도가 아니더라도, JobQ1의 작업 전부다 소모되는시간이 JobQ2의 작업 몇개를 수행하는것보다 빠르다면, 충분히 이런상황이 발생할 수 있다고 생각합니다) 다른 예시로 JobQ1에는 어마어마한 양의 일감이 쌓여있는상태에서 A가추가가 되고, JobQ2에는 아무것도 없을때 B가 바로 실행이 될텐데, 이경우에도 A가 먼저 패킷이 도달했지만 B에 비해 아주 오랜 뒤에 처리가 될수 있을것 같다고 생각해서 이런경우 클라 입장에서는 분명 먼저 요청을 했는데 버그가 발생했다고 느낄수도 있다고 생각해서 질문드립니다. 두번째로 궁금한점은 Part4 과정이 거의 끝나가고 있어서 질문드리는 것인데요, 제가 메인스레드가 1개인것만 해봐서 멀티스레드를 어떻게 인게임컨텐츠에서 사용해야할지 감이 안잡혀서, 멀티스레드로 인게임 컨텐츠 부분의 객체를 접근할때, 락을 사용하는 예제가 있는 강의가 있는지 궁금합니다. 이과정이 끝나면 Part5를 바로 이어서 할예정입니다. 감사합니다
질문입니다! 상황 1 . 우선 NetworkClientExceptionV5는 RuntimeException을 상속 받은 상태입니다. public class NetworkClientExceptionV5 extends RuntimeException { public NetworkClientExceptionV5(String message) { super(message); } } 그러면 NetworkClientExceptionV5를 상속받은 ConnectException과 SendException도 RuntimeException이 되는 거구요. public class ConnectExceptionV5 extends NetworkClientExceptionV5 { private final String address; public ConnectExceptionV5(String address, String message) { super(message); this.address = address; } public String getAddress() { return address; } } public class SendExceptionV5 extends NetworkClientExceptionV5 { private final String data; public SendExceptionV5(String data, String message) { super(message); this.data = data; } public String getData() { return data; } } 상황 2 . 그리고 NetworkClientV5는 AutoCloseable을 구현했습니다. import exception.ex5.exception.ConnectExceptionV5; import exception.ex5.exception.SendExceptionV5; public class NetworkClientV5 implements AutoCloseable { private final String address; private boolean connectError; private boolean sendError; public NetworkClientV5(String address) { this.address = address; } public void connect() { if (connectError) { throw new ConnectExceptionV5(address, address + " 서버 연결 실패"); } // 연결 성공 System.out.println(address + " 서버 연결 성공"); } public void send(String data) { if (sendError) { throw new SendExceptionV5(data, address + " 서버에 데이터 전송 실패 : " + data); } // 전송 성공 System.out.println(address + " 서버에 데이터 전송 : " + data); } public void disconnect () { System.out.println(address + " 서버 연결 해제"); } public void initError(String data) { if (data.contains("error1")) { connectError = true; } if (data.contains("error2")) { sendError = true; } } @Override public void close() throws Exception { System.out.println("NetworkClientV5.close"); disconnect(); } } ==질문입니다!!== NetworkServiceV5에서 public class NetworkServiceV5 { public void sendMessage(String data) { String address = "http://example.com"; try (NetworkClientV5 client = new NetworkClientV5(address)) { client.initError(data); // 추가 client.connect(); client.send(data); } } } try (NetworkClientV5 client = new NetworkClientV5(address))에서 컴파일 오류가 납니다. client에서 올라오는 예외들은 다 RuntimeException이므로 따로 throws를 안 해줘도 되는데 왜 해결하려고 하면 throws를 선언하거나 catch를 해야한다고 하는 건가요? RuntimeException은 예외를 throws 선언없이 자동으로 밖으로 던지기에 catch를 안 해도 상관이 없을텐데 왜 alt + enter을 누르면 throws 선언과 catch를 해야만 된다고 하는 건지 이해가 가지 않습니다. 제가 생각하는 건 이런식으로 흘러간다고 생각했습니다. main()에서 (error1 | error2)를 입력 받음 -> sendMessage를 호출 -> sendMessage에서 코드 진행 -> (client.connect | client.send)를 만나서 NetworkClientV5에 들어감 -> (ConnectException | SendException)을 터트림 -> NetworkClientV5에서 예외를 밖으로 던짐 (런타임이라서 throws 선언 안 해도 던져짐) -> NetworkService로 예외가 날라옴 -> 여기서도 처리가 불가능 하니, 예외를 밖으로 던짐 (여기서 throws 선언을 안해도 던져져야 한다고 생각함. 날아온 예외는 RuntimeException이기 때문에) -> main으로 예외가 넘어옴 -> main에는 catch (Exception e)가 있어서 예외를 잡고 정상흐름으로 돌아옴. 마지막은 메인메서드 입니다! import exception.ex5.exception.SendExceptionV5; import java.util.Scanner; public class NetworkmainV5 { public static void main(String[] args) { NetworkServiceV5 service = new NetworkServiceV5(); Scanner scanner = new Scanner(System.in); while (true) { System.out.print("전송할 문자 : "); String input = scanner.nextLine(); if (input.equals("exit")) { break; } try { service.sendMessage(input); } catch (Exception e) { exceptionHandler(e); } System.out.println(); } System.out.println("프로그램을 종료합니다."); } // 공통 처리 예외 private static void exceptionHandler(Exception e) { System.out.println("사용자 메시지 : 죄송합니다. 알 수 없는 문제가 발생했습니다."); System.out.println("==개발자용 디버깅 메시지=="); // 스택 트레이스 출력 e.printStackTrace(System.out); // 스택 트레이스 출력 System.err //e.printStackTrace(); // 필요하면 예외별로 별도의 추가 처리 가능 if (e instanceof SendExceptionV5 sendEx) { System.out.println("[전송 오류] 전송 데이터 = " + sendEx.getData()); } } }
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요. 1. 강의 내용과 관련된 질문을 남겨주세요. 2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요. (자주 하는 질문 링크: https://bit.ly/3fX6ygx) 3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요. (질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG) 질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요. ========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예) [질문 내용] 이전에 데이터베이스에 반영되는 실행 시점은 em.flush() 트랜잭션 커밋 JPQL 쿼리 실행 으로 알고 있습니다. 그런데 11:45초 쯤부터 em.persist(team) 을 실행할때 id 값을 갖고 오기위해 new Member 이전에 insert문이 실행된다면 위의 경우가 아닌 것으로 판단되는데 em.persist 호출해도 쓰기 지연 SQL에 쌓이는 것이 아닌 바로 쿼리를 실행하게 되는 건가요?