Web Server

  • 정적 리소스(HTML, CSS, JS, 이미지) 제공
  • Application 로직을 포함할 수 있지만 특화는 WAS

 

WAS(Web Application Server)

  • 웹 서버의 기능 포함 + Application 로직 수행 + DB와 상호작용 => 동적 컨텐츠 생성 (톰켓,,)
  • Java에서는 Servlet Container 기능을 제공하면 WAS

 

실제 웹 시스템 구성

  • 정적은 웹 서버, 동적은 WAS
  • 웹 서버는 오류가 날 일이 적이 때문에 오류 화면을 보여줄 수 있음

 

Servlet

만약, 로그인을 할 때 실제로 전달되는 데이터는 아래처럼 HTTP메시지로 주고 받아짐 -> 그렇기에 서버는 서버와 TCP/IP 연결....부터 시작하면서 HTTP Message와 관련된 작업과 비지니스로직, 연결 등을 해 줘야 함

HTTP 프로토콜 기반 요청(Request) 및 응답(Response)을 처리 -> HTTP통신과 관련된 작업들을 대신해 줌으로써 개발자는 비지니스 로직만 할 수 있게 도와줌

 

Servlet 동작 순서

1. WAS는 HTTP 요청 메시지를 기반으로 요청, 응답 객체 생성해서 서블릿 컨테이너에 넘겨주고 , ExampleServlet 객체 호출

2. ExampleServlet에서 비지니스 로직 처리하고, 응답에 필요한 정보 개발자가 입력

3. WAS는 Response 객체 정보(개발자가 입력한 정보)로 HTTP 응답 메세지 생성

 

개발자가 하는 일

 Request 객체에 담겨져있는 HTTP 요청 정보를 꺼내서 사용 -> 비지니스 로직 수행 -> 응답 Response 객체에 HTTP 응답 정보를 입력

@WebServlet("/products")
public class ProductServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 1. 요청 정보 꺼내기 (Request 객체 사용)
        String method = request.getMethod();  // HTTP Method (GET)
        String url = request.getRequestURI(); // 요청 URL (/products)

        System.out.println("요청 메서드: " + method);
        System.out.println("요청 URL: " + url);

        // 2. 비즈니스 로직 처리 (상품 목록 생성)
        List<String> products = List.of("Laptop", "Smartphone", "Headphones");

        // 3. 응답 정보 입력 (Response 객체 사용)
        response.setContentType("application/json"); // 응답 타입 설정
        response.setCharacterEncoding("UTF-8");      // 인코딩 설정
        PrintWriter out = response.getWriter();

        // JSON 형식으로 응답 데이터 작성
        out.println("{\"products\": [");
        for (int i = 0; i < products.size(); i++) {
            out.print("\"" + products.get(i) + "\"");
            if (i < products.size() - 1) {
                out.print(", ");
            }
        }
        out.println("]}");
    }
}

 

Servlet Container

서블릿을 초기화, 생성, 관리, 호출, 종료하는 역할을 수행 -> Servlet 객체를 싱글톤으로 관리

동시 요청에 대한 처리를 위해 Multi Thread를 지원

 

종료 : WAS(서블릿 컨테이너 포함)가 종료될 때 Servlet도 함께 종료

생성 : 코드만 작성하면 서블릿 컨테이너가 생성

 

WAS는 Multi Thread를 지원 -> Thread Pool을 통해서 

 

Thread Pool

Thread는 Thread Pool에 보관 및 관리

Thread Pool에 생성 가능한 Thread 최대치를 관리 Tomcat은 최대 200개 기본 설정 -> 테스트로 최적의 수 찾아서 적용

  • 장점
    1. 요청 마다 Thread를 생성하는 단점을 보완하였다.
    2. Thread가 미리 생성되어 있어서 Thread를 생성, 종료하는 비용이 절약된다. → 응답이 빠름
    3. 생성 가능한 Thread 최대치가 제한되어 있어서 많은 요청이 들어와도 안전하게 처리할 수 있다.
  • 단점
    1. Thread Pool의 최대 Thread 수를 낮게 설정한다면 응답이 지연된다.
    2. 최대 Thread 수가 너무 높으면 즉, ⭐️요청이 많아지면 리소스 부족으로 서버가 다운된다.⭐️

 

SSR과 CSR차이

SSR : 서버에서 동적인 페이지 완성

CSR : HTTP API 통신으로 얻은 결과를 통해 브라우저에서 동적으로 페이지 완성

 

네트워크 기본 기식

인터넷 : 컴퓨터 네크워크 통신망

ip : 인터넷 프로토콜, 즉 통신 규약, ip주소는 ip에 필요한 고유 주소

인터넷 통신 시에는 지정한 IP 주소에 데이터를 Packet 이라는 단위로 전달

 

TCP

  • 신뢰성 통신이라 생각하면 됨, 키워드는 신뢰! -> 데이터 잘 받았어요!, 패킷 순서보장해요! 
  • 3 Way HandShake 수행, syn보내면 ack로 잘 받았다고 응답하는 과정, ack는 잘받았고 너의 요청을 수락한다는 의미, 그래서 클라이언트는 ack와 함께 데이터를 전송함 -> 이 과정을 통해서 신뢰성을 보장하지만 대신 시간이 오래걸린다 ㅜㅠ

 

UDP

  • TCP랑 반대라고 보면 됨, 비신뢰성 비연결성 하지만!  3Way HandShake가 없기때문에 속도가 빠름
  • 데이터 무결성 검사하는 체크섬(Checksum)을 포함하기 때문에 잘못된 데이터가 전송되지 않도록하긴함
  • 속도빠르니까 스트리밍, 게임에 많이 사용~

PORT

  • 그냥 간단하게 내 컴퓨터 아이피 주소는 딱 하나임 근데 나는 컴터에서 겜도 하고 인터넷도 하고 카톡도 함 그럼 패킷이 겜으로 갈지 카톡으로 갈지를 모름 ->  그 프로세스를 구분하기 위해서 PORT가 존재!
  • 이미 사용되고 있는 포트 (0 ~ 1023) -> 여긴 피해서 사용하기, 80번은 HTTP

크롬 브라우저에 유튜브 URL을 입력했을 때 발생하는 일

1. DNS를 조회해서 해당  IP주소를 응답받음

2. 웹 브라우저에서 구글 서버로 유튜브 검색해줘!라는 내용의 HTTP요청 메시지가 담긴 요청 패킷을 보냄

3. 구글 서버에서 받은 HTTP요청 메시지를 기반으로 응답 HTTP메시지를 만들어 응답

4. 응답 패킷이 도착하면 HTML이 응답으로 오고 브라우저에 그려짐

 

Scale Up, Scale Out

Scale Up : 단일 서버의 하드웨어 사용을 높임, 처리를 더 빠르게 함

Scale Out : 같은 사영의 서버를 여러개 배치, 더 맣은 사용자 요청을 동시 처리 가능

 

Stateful, Stateless

Stateful : 서버는 클라이언트의 상태를 유지(기억)함. -> 그렇기에 같은 서버가 유지되야 함 -> 그렇기에 에러나면 큰일

Stateless : 클라이언트의 상태를 유지하지 않음 -> 같은 서버를 유지할 필요가 없기에 Scale Out 수평 확장성이 높음 -> 근데 로그인처럼 상태를 유지해야하는 경우가 생김. 그래서 그럴땐 뭐다? 쿠키 세션 토큰을 활용하여 극복!

 

Connection, Connectionless

Connection(연결) : 서버는 클라이언트와 계속해서 연결을 함 -> 새로운 연결 과정을 거치지 않기에 속도는 빠르지만 연결을 위한 자원이 많이 소모됨

Connectionless(비연결) : 서버는 클라랑 연결 유지 안 함 -> 자원을 효율적으로 사용하지만 요청이 오면 3Way HandShake 해야 되서 시간 걸림, 게다가 정적 자원 모두 다시 다운하기에 느림~> 그래서 캐싱 혹은 HTTP 지속연결로 해결

 

HTTP 지속연결(Persistent Connections)

하나의 요청에 필요한 요청들이 모두 응답될 때 까지 연결을 유 즉, 정적 자원을 다 가지고 올 때까지 계속 연결 유지함, 비지속 연결은 정적자원에 대해서 하나씩 응답하고 끊고 연결

 

HTTP(HyperText Transfer Protocol)

  • 요청 응답 구조
  • 무상태 (Stateless) -> 클라이언트의 상태를 보존하지 않음, 로그인은 쿠키 토큰 세션으로 해결
  • 비연결 (Connectionless) -> 서버 자원을 효율적, 정적자원은 계속 다시 받고 속도 오래걸림 -> HTTP 지속연결(Persistent Connections)로 문제를 해결

 

HTTP Message 구조

HTTP Message 구조

 

 

HTTP 요청 메세지(Request Message)

  • Start Line
    • HTTP Method(GET ...)
    • path(HTTP Request가 전송되는 대상, Query String), /search?keyword=yena
    • HTTP Version : 1.1
  • Header
    • Host
    • field-name: OWS(띄어쓰기) field-value 구조 -> Content-Type : text/html  
  • Empty Line : 공백 한 줄, 필수값
  • Message Body : 실제 전송하는 데이터가 담겨 있는 부분

 

HTTP 응답 메세지(Response Message)

 

 

 

Method

POST : Message Body를 통해 요청 데이터를 전달, 멱등성(여러 번 실행해도 동일한 결과)을 보장하지 않는다

GET : 서버에 추가적인 데이터 전송을 해야한다면, Message Body가 아닌 Query String(Query Parameter)를 사용

PUT : 리소스 덮어쓰기, 클라이언트 측에서 리소스를 식별하여 URI를 지정, 없으면 생성 있으면 아예 덮어씀

PATCH : 리소스 부분 업데이트

 

HTTP Status Code

  • 1xx (정보)
  • 2xx (성공)
  • 3xx (리다이렉션)

  • 4xx (클라이언트 에러)
  • 5xx (서버 에러)
  •  

 

HTTP API 설계 방법

  1. HTTP API는 설계시 항상 리소스 식별을 기준
  2. URI에 들어갈 리소스는 복수 형태 board → boards
  3. URL에 동사를 사용하지 않는다.
  4. HTTP Method의 역할을 URL에 포함하지 않는다.

Restful API : REST 기반으로 서비스 API를 구현한 것

REST : URI를 통해 자원(Resource)을 명시하고, HTTP Method(POST, GET, PUT, DELETE,PATCH 등)를 통해 해당 자원에 대한 CRUD Operation을 적용하는 것

 

구현 규칙

  1. 리소스는 명사를 사용
  2. 복수 형태를 사용
  3. 만약, REST만으로 해결하기 어려운 경우라면 동사를 허용
  4. 자원의 계층 관계를 슬래시(/)로 표현
  5. 마지막 문자에는 슬래시(/)가 있으면 안된다.
  6. 하이픈(-)을 사용
  7. 소문자
  8. URI에 파일 확장자를 포함하면 안된다.
  9. CRUD 함수명은 사용하지 않고, HTTP Method를 활용
  10. 정렬, 필터링, 페이징은 신규 API를 만드는것이 아닌 Query Parameter를 사용
GET /users/123     // 특정 사용자 조회

POST /users        // 사용자 생성
{
    "name": "sparta",
    "password": "codingclub"
}

PUT /users/123     // 사용자 정보 수정
{
    "name": "java",
    "password": "spring"
}

DELETE /users/123  // 사용자 삭제

 

접근 방법

  • 1. 먼저 연결 정보를 입력 받기
n = int(input())
m = int(input())
# 1. 연결 정보에 대해 입력 받기
con = []
for _ in range(m):
    a, b = map(int, input().split())
    con.append((a, b))

# con = [[1, 2], [2, 3], [3, 5] ....]

 

  • 2. 방문 여부를 확인하는 리스트 생성
visited = [False] * (n+1)

 

  • 3. 연결 정보 -> 그래프로 표현 (양방향 연결)
graph = {}
    for i in range(1, n+1):
        graph[i] = []
    # 결과 : {1: [], 2: [], 3: [], 4: [], 5: []}
    for a, b in con:
        graph[a].append(b)
        graph[b].append(a)

 

  • 4. 깊이 우선 탐색
def DFS(node):
        # 방문한 노드는 true로 표시
        visited[node] = True
        count = 1 # 방문한 노드는 감염되었기에 숫자 1증가
        for n in graph[node]:
            if not visited[n]:
                count += DFS(n) #방문한 노드의 이웃 노드가 탐색을 안한 곳이라면 그곳을 또 탐색
        return count

    result = DFS(1)
    return result -1

 

 

정답

n = int(input())
m = int(input())
# 1. 연결 정보에 대해 입력 받기
con = []
for _ in range(m):
    a, b = map(int, input().split())
    con.append((a, b))

# con = [[1, 2], [2, 3], [3, 5] ....]
def count(n, con):
    # 2. 방문 여부를 확인하는 리스트 생성
    visited = [False] * (n+1)
    graph = {}
    for i in range(1, n+1):
        graph[i] = []
    # 결과 : {1: [], 2: [], 3: [], 4: [], 5: []}
    # 3. 연결 정보 -> 그래프로 표현 (양방향 연결)
    for a, b in con:
        graph[a].append(b)
        graph[b].append(a)

    def DFS(node):
        # 방문한 노드는 true로 표시
        visited[node] = True
        count = 1 # 방문한 노드는 감염되었기에 숫자 1증가
        for n in graph[node]:
            if not visited[n]:
                count += DFS(n) #방문한 노드의 이웃 노드가 탐색을 안한 곳이라면 그곳을 또 탐색
        return count

    result = DFS(1)
    return result -1

print(count(n, con))

'Algorithm' 카테고리의 다른 글

시저 암호 / 프로그래머스  (1) 2025.01.27
최소직사각형 / 프로그래머스  (0) 2025.01.24
크기가 작은 부분 문자열 / 프로그래머스  (1) 2025.01.21
5430 AC / 큐  (0) 2025.01.18
10866 덱 / 큐  (0) 2025.01.17

삼총사

def solution(number):
    answer = 0
    for i in range(0, len(number)):
        for j in range(i+1, len(number)):
            for k in range(j+1, len(number)):
                if number[i] + number[j] + number[k] == 0:
                    answer += 1
    return answer

 

크기가 작은 부분 문자열

  • 첫 번째 시도 -> 하지만 테스트 케이스에서 시간 초과 및 빈 객체에 pop했다는 에러 뜸
  • len(deq) >= len(p) 이  조건 또한 빼먹음
import collections
def solution(t, p):
    answer = 0
    deq = collections.deque(t)
    while len(deq) >= len(p):
        d = ""
        copy = ""
        recover = []
        for _ in range(len(p)):
            d += deq.popleft()
        copy = d[:]
        if int(p) >= int(copy):
            answer += 1
        recover = list(d)

        for _ in range(2):
            deq.appendleft(recover.pop())


    return answer
  1. 크기가 작은 부크기가 작은 부분 문자
  • 두 번째 시도
    • 그냥 덱이고 뭐고 슬라이싱을 하면 되는 거였다..
    • 뭔가 괜히 어렵게 생각하는 습관이 있는 것 같다;; (아직 개념적인 걸? 잘 몰라서 그런 것 같음)
import collections

def solution(t, p):
    answer = 0
    t_len = len(t)
    p_len = len(p)
    start = 0
    end = p_len

    while t_len - p_len >= 0:
        n = int(t[start:end])
        if n <= int(p):
            answer += 1
        start += 1
        end += 1
        t_len -= 1

    return answer

'Algorithm' 카테고리의 다른 글

최소직사각형 / 프로그래머스  (0) 2025.01.24
2606 바이러스 / DFS,BFS  (0) 2025.01.22
5430 AC / 큐  (0) 2025.01.18
10866 덱 / 큐  (0) 2025.01.17
1966 프린터 큐 / 큐  (0) 2025.01.16

결론부터 말하자면 이번 과제는 많이 부족했던 것 같다🙈

하지만 어쩌겠는가,,, 이미 시간은 지났고 되돌릴 수 없다

지금부터 고민했던 점들에 대해서 정리해보도록 하고 다음 프로젝트는 이번 문제점을 고쳐서 발전시키는 방향으로 가자😳😳😳

 

고민점

장바구니 기능을 클래스로 빼는게 맞는지에 대한 고민

  • 장바구니기능이 없는 lv5만 구현했을 때까지만 하더라도 Kiosk 클래스는 사용자의 주문에 대한 입출력에 대해서 기능적인 부담이 크지 않다고 생각
  • 하지만, 장바구니 요구사항을 보니 내용이 꽤 많았음.. (추가 삭제 조회 등등, + 예외 처리)
  • 그렇기 때문에 이 모든 요구사항을 Kiosk에 넣어버리면 코드도 엄청 무거워지고 알아보기가 너무 힘들 것 같다고 생각

-> 장바구니와 관련된 역할은 Cart 클래스에서 하기로 결정!

public class Cart {
    private List<MenuItem> cartItems = new ArrayList<>(); // 장바구니에 넣은 물건을 담는 리스트
    private double total;
    private Scanner sc = new Scanner(System.in);

    public void addCart(MenuItem menuItem){
    // 장바구니에 물건 추가
    }

    public void showCartItems() {
	// 장바구니에 담긴 물건 조회
    }

    public void showTotalCost() {
    // 장바구니 물건 합계 출력
    }

    public void showCartAddMsg(MenuItem menuItem) {
    // 장바구니에 메뉴를 추가할지 묻는 메서드
    }

    public void deleteCartItem() throws MenuItemNotFoundException{
    // 장바구니 메뉴 삭제
    }

    public void cancelCart() {
    // 장바구니 물건 전체 삭제
    }

    public double getTotal() {
    }

    public boolean isCartEmpty(){
    }

}

 

 

작은 기능들까지도 메서드로 분리하는게 맞는 것인지에 대한 고민 + 여전한 Kiosk의 막대한 책임

  • 요구조건에 맞게 구현하려다 보니 start()메서드가 너무 방대하고 복잡해져 버림
  • 더군다나 출력에 관한 것도 너무 많고 지져분해져서 개발을 하면서도 너무 복잡하게 느껴짐 (아래보다 훠얼씬 많음 ㅠ_ㅜ)

  • 그러한 이유로 먼저 출력에 관한 메서드들을 생성해서 분리시킴 -> 확실히 가독성 측면에서 훨씬 깔끔했고, start()메서드의 길이가 줄어서 개발할 때 중요도가 낮은 출력 메서드들의 내부 로직을 볼 필요가 없어서 한결 편했음

하지만 이렇게 자잘하게 메서드를 분리해서 만드는 것이 과연 옳은지 고민이 된다...

역할이라고 하기엔 너무 하찮지 않나 싶은 생각도 들고, 어쩌면 태초로 돌아가서 객체지향 설계가 잘못된 것 같다는 생각이 들기도 했다

키오스크 클래스에 모든 책임들이 너무 방대하게 있다는 생각이 쉽사리 지워지지 않았다

 

메서드 분리 후에도 여전한 문제점

  • 주석 없이도 코드에 대해서 이해하고 파악할 수 있어야 하는데 도통 주석 없이는 이해하기 힘들다는 생각
  • 메서드로 최대한 많이 분리했지만 결국 start메서드 자체는 여전히 복잡함
  • Kiosk 의 책임은 여전히 너무나 큼

설계의 잘못됨(?)

하지만 또 생각해보면..

사용자에게 메뉴를 보여주고 입력을 받고 흐름을 제어하는 것 => Kiosk

장바구니와 관련된 역할을 수행하는 것 => Cart

가 아닐까 생각이 들기도 한다. 아니면 역할을 애초에 사용자의 키오스크 화면 상에서 카테고리와 관련된 로직을 수행하는 클래스, 메뉴와  관련된 로직을 수행하는 클래스, 장바구니와 관련된 로직을 수행하는 클래스 이런식으로 3개를 나누어서 설계를 했어야 하나 싶은 고민이 들기도 한다...

이번 키오스크 프로젝트에서 과연 객체지향적으로 적절하게 설계한 답이 너무 궁금하다 (나오면 바로 분석해야지)

 

 

초기화 메서드를 최초 한번만 실행하기 위한 방법에 대한 고민

  • menuItems의 리스트 인덱스를 생성을 딱 한번만 하기 위해서 어떤 식으로 할지 고민하다가 showMenuItems 메서드를 실행할 때 리스트 인덱스가 비어있으면 생성하고, 비어있지 않다면 생성하지 않는 식으로 구현
public void showMenuItems(){
        if (menuItemIndexList.isEmpty()){
            setMenuItemIndex();
        }
        for(int i = 0; i < menuItems.size(); i++){
            System.out.println( i + 1 + ". " + menuItems.get(i).getName() + " | W " + menuItems.get(i).getPrice() + " | " + menuItems.get(i).getDescription());
        }
    }

    public void setMenuItemIndex() {
        for(int i = 0; i < menuItems.size(); i++){
            menuItemIndexList.add(i+1);
        }
    }

 

그 밖의 메모해뒀던 여러 잡다한 고민들...

하지만 내가 생각한 이 고민들의 근원은 미약한 설계 이다.

결국 클래스 설계부터 기타 로직까지 충분한 시간을 가지고 생각하고 구현을 했어야 했는데 그냥 냅다 기존에 진행했던 과제를 기반으로 신나게 기능들만 열심히 구현을 해서 이런 생각이 든 것 같다. (그래서 막상 코드가 너무 복잡하고 더러워졌다. 그냥 절차지향의 코드인듯)

알고리즘을 풀 때도 종이랑 펜으로 먼저 설계라고 코딩을 하듯이 앞으로 프로젝트도 설계를 좀 더 주의깊게 한 다음에 코딩을 시작해야겠다!

 

Enum과 할인 적용

  • number(할인 정보 인덱스 번호), type(할인 타입), discountRate(할인률)로 구성
  • 할인을 적용하는 메서드(discount)는 추상메서드로 두고, 각각의 할인 객체는 이를 오버라이딩
  • 할인 정보를 출력하는 메서드(showInformation)와 사용자로부터 입력받은 값이 어떤 할인 객체에 해당되는지 확인하는 메서드(fromDiscount)는 정적(static)으로 선언 
  • fromDiscount 메서드에서 해당되는 할인 객체를 찾으면 해당 객체에서 discount를 실행한 후, 값을 바로 return
package org.example.kiosk.challenge;

import org.example.kiosk.common.exception.InvalidInputRangeException;

public enum Discount {
    NATIONALHONOREE("1", "국가유공자", 0.1) {
        @Override
        public double discount(double total) {
            return total - total * getDiscountRate();
        }
    },
	// 군인..학생..등등

    private final String number; // 할인 정보 인덱스 번호
    private final String type; // 할인 타입
    private final double discountRate; // 할인률

    public String getNumber() {
        return number;
    }

    public String getType() {
        return type;
    }

    public double getDiscountRate() {
        return discountRate;
    }

    Discount(String number, String type, double discountRate) {
        this.number = number;
        this.type = type;
        this.discountRate = discountRate;
    }

    // 각 클래스에 맞게 할인률이 적용되어 할인을 진행할 메서드
    public abstract double discount(double total);

    // 입력받은 문자열과 매칭되는 클래스인지 확인 후, 해당 할인 클래스의 할인을 수행시킨 후 적용된 할인값을 return
    public static double fromDiscount(String inputNumber, double total) throws InvalidInputRangeException{
        for (Discount discount : values()) {
            if (discount.getNumber().equals(inputNumber)) {
                return discount.discount(total);
            }
        }
        // 해당 할인 정보 화면에서 존재하지 않는 값을 입력했다면 에러 발생
        throw new InvalidInputRangeException();
    }

    // 할인 정보를 출력하는 메서드, 굳이 인스턴스화 시킬 필요가 없을 것 같아서 static으로 선언
    public static void showInformation(){
    }
}

 

Exception

  • InvalidInputException : 숫자가 아닌 다른 값을 입력할 경우 발생하는 예외 객체
  • InvalidInputRangeException : 화면에 출력된 값이 아닌 다른 숫자를 입력할 경우 발생하는 예외 객체
  • MenuItemNotFoundException : 메뉴를 삭제할 때, 장바구니 리스트에 없는 이름을 입력한 경우 발생하는 예외 객체

 

 


다시 결론적으로 앞으로는 설계를 충분히 고민하고 구현을 시작하자!!

 

핵심

  • 덱을 R이 나올 때마다 뒤집으면 시간 초과가 뜸 -> 길이(N)만큼 걸리기 때문
  • 뒤집힌 상태인지 확인하는 변수를 만들어서 체크하면서 해결해야 함
  • 길이가 0인 리스트가 들어오면 덱을 초기화 시켜줘야 함

문자열 다루기

  • arr = input()[1:-1].split(',') : 입력받은 문자열에서 양옆제외하고 읽어온 후 ,을 기준으로 분리해서 값들을 배열로 저장
  • deq = collections.deque(arr) : 배열 통째로 덱에 추가
  • result = "[" + ",".join(map(str, deq)) + "]" , 을 사용하여 원소사이를 붙여줌
import collections
t = int(input())
ans = []

for _ in range(t):
    ac = input()
    n = int(input())
    arr = input()[1:-1].split(',')

    deq = collections.deque(arr)

    flag = 0 # 뒤집은 상태인지 아닌지 확인하는 변수, 홀수면 뒤집은 것
    err_count = False

    if n == 0: # 길이가 0이라면 빈 덱으로 초기화
        deq = []

    for i in ac:
        if i == "R":
            flag += 1

        if i == "D":
            if len(deq) == 0:
                ans.append("error")
                err_count = True
                break
            else:
                if flag % 2 == 0:
                    deq.popleft()
                else:
                    deq.pop()

    if not err_count:
        if flag % 2 == 0:
            result = "[" + ",".join(map(str, deq)) + "]"
            ans.append(result)
        else:
            deq.reverse()
            result = "[" + ",".join(map(str, deq)) + "]"
            ans.append(result)

for i in ans:
    print(i)

'Algorithm' 카테고리의 다른 글

2606 바이러스 / DFS,BFS  (0) 2025.01.22
크기가 작은 부분 문자열 / 프로그래머스  (1) 2025.01.21
10866 덱 / 큐  (0) 2025.01.17
1966 프린터 큐 / 큐  (0) 2025.01.16
이상한 문자 만들기 / 프로그래머스  (1) 2025.01.15

import collections

class Deq:

    def __init__(self):
        self.deque = collections.deque()

    def push_front(self, value):
        self.deque.appendleft(value)

    def push_back(self, value):
        self.deque.append(value)

    def pop_front(self):
        if len(self.deque) == 0:
            return -1

        return self.deque.popleft()

    def pop_back(self):
        if len(self.deque) == 0:
            return -1

        return self.deque.pop()

    def size(self):
        return len(self.deque)

    def empty(self):
        if len(self.deque) == 0:
            return 1
        else:
            return 0

    def front(self):
        if len(self.deque) == 0:
            return -1
        else:
            return self.deque[0]

    def back(self):
        if len(self.deque) == 0:
            return -1
        else:
            return self.deque[-1]

deque = Deq()
n = int(input())
q = []
for _ in range(n):
    q.append(input().split())

for i in q:
    if i[0] == "pop_front":
        print(deque.pop_front())
    if i[0] == "pop_back":
        print(deque.pop_back())
    if i[0] == "size":
        print(deque.size())
    if i[0] == "empty":
        print(deque.empty())
    if i[0] == "front":
        print(deque.front())
    if i[0] == "back":
        print(deque.back())
    if i[0] == "push_front":
        deque.push_front(int(i[1]))
    if i[0] == "push_back":
        deque.push_back(int(i[1]))

'Algorithm' 카테고리의 다른 글

크기가 작은 부분 문자열 / 프로그래머스  (1) 2025.01.21
5430 AC / 큐  (0) 2025.01.18
1966 프린터 큐 / 큐  (0) 2025.01.16
이상한 문자 만들기 / 프로그래머스  (1) 2025.01.15
11866 요세푸스 문제 0 / 큐, 덱  (1) 2025.01.15

+ Recent posts