0311 TIL

looggi·2023년 3월 11일
0

TILs

목록 보기
32/114
post-thumbnail

웹개발의 패러다임

옛날 옛날 먼 옛날

웹 개발의 페러다임이 백엔드에서 프런트앤드로 옮겨오면서 예전보다 많은 코드가 서버가 아닌 브라우저에서 실행되고 있습니다. ex.*템플릿 렌더링

  • HTML 템플릿: client-side 렌더링 vs. server-side 렌더링 (2014)

동적으로 렌더링하는 부분을 제외한 초기 페이지는 server-side 렌더링하고 동적인 부분만 client-side 렌더링
https://www.clien.net/service/board/park/5699595

반응형 프로그래밍

데이터를 가져와서 화면을 만드는 것이 아닌
미리 선언되어있는 구조에서 템플릿으로 데이터를 전달(변경된 데이터를 감지하고 전달)
EventListener 중심
https://yozm.wishket.com/magazine/detail/1334/

네트워크

쿠키

서버가 어떤 데이터를 브라우저 측에 저장한 후 다시 그 데이터를 받아오는 기술, 또는 그 데이터 자체
클라이언트(브라우저)와 서버가 네트워크를 통해 쿠키를 주고받기 위해 HTTP 프로토콜을 만들었고, 이에 의해 쿠키는 HTTP 헤더에 담아 전송하기로 약속됨
클라이언트가 최초의 연결 요청을 보낼 때 서버가 쿠키를 set-cookie에 담아 보내줌

  • set-cookie하나에 하나의 값만 담을 수 있음
    set-cookie 자체가 여러 개인 것은 가능함.
  • 유효기간이 명시되어있음 (→ 영속 쿠키)
    • 세션 쿠키: 유효기간이 명시되지 않고 세션 종료시 만료됨
      탬/ 창을 닫을 때 세션이 만료되어 쿠키도 같이 만료되는 구조
  • 키를 즉시 만료하고 싶을 때는Max-Age=0
  • Domain=test.com 을 명시하면 해당 도메인의 서브도메인까지 쿠키 범위 확장
  • Path=/user 명시하면 해당 경로를 포함하는 url에 대해서만 쿠키를 공유
  • Secure 속성을 명시하면 해당 쿠키는 https 프로토콜 상에서만 쿠키를 서버로 전송
  • HttpOnly 브라우저에서 자바스크립트로 Document.cookie를 통해 접근할 수 없음

이후 요청시 클라이언트는 cookie에 쿠키를 담아 보냄 (여러 개를 보낼 땐 ;로 구분해서 보낼 수 있음)

  • 유효기간이 set-cookie에 의해 설정됨

브라우저는 사용자가 www.test.com이라는 같은 도메인에 머무는 한 /index.html을 방문하든 /about.html을 방문하든 /contact.html을 방문하든 매번 같은 쿠키를 돌려준다

➡️ 동일한 도메인에서는 요청시 전송하는 쿠키가 동일하다 ⭐⭐⭐⭐⭐⭐

🍪 쿠키의 활용

🥛같은 도메인에서 같은 쿠키 (동일한 호스트)를 보내므로 http의 stateless 특성에도 불구하고 요청을 보낸 브라우저를 구분할 수 있다

🥛로드 밸런서에 도움
소규모의 서비스에서 많은 사용자들이 서비스를 이용할 때 항상 같은 서버에 요청을 보낼 수가 없음
ex. 로그인 요청 서버≠게시글 작성 요청 서버
이 때 서버를 인식할 수 있도록 해당 브라우저가 어떤 서버에 요청을 보내면 될 지 set-cookie에 serverId를 보내주면 이후에 계속 같은 서버로 요청을 보내는 것이 가능하다

  • 대규모의 서비스에서는 서버간 공유하는 DB에 세션 정보를 저장한다

🥛세션 ID 저장

🥛좀비 쿠키
유효 기간을 엄청 길게 하거나 자바스크립트를 이용한 다른 편법들을 써서 아무리 브라우저에서 삭제를 해도 계속 부활하는 쿠키

🥛서드파티(third party) 쿠키
타 도메인을 상대로 적용되는 쿠키가 넘어오는 경우
사용자 맞춤 광고를 위한 추적에 활용

🍪 쿠키의 한계

중요한 데이터를 쿠키를 사용해서 브라우저에 저장하면 유실되기 쉽다

서버에서는 브라우저로 부터 수신한 쿠키 데이터가 유효한지 검증할 필요가 있다

매 요청마다 같은 데이터가 서버로 전송됨에 따른 네트워크 대역폭 낭비

https://www.daleseo.com/http-cookies/
https://www.daleseo.com/http-session/

서버 자원이 풍족한 현재에는 순수하게 데이터를 클라이언트 측에 저장하기 위한 용도로써의 쿠키는 입자가 많이 좁아짐 → 웹 스토리지 기술

웹 스토리지

브라우저에 데이터를 저장하는 기술

  • 중요하지 않은 데이터를 DB나 클라우드에 저장하는 것은 자원의 낭비이므로 클라이언트 단에 저장한다
    • 다른 기기나 브라우저 간에 데이터가 공유되고 영속되야 한다면 클라우드(Cloud) 플랫폼이나 데이터베이스(DB) 서버를 사용해야함

문자열 데이터만 저장

  • 다른 타입의 데이터를 문자형으로 변환해서 저장

  • JSON을 이용해서 저장시 stringify해서 저장하고 가져올 때 parse해서 읽어들이면 원본 데이터를 얻을 수 있다

> localStorage.setItem('sth', JSON.stringify({a: 1, b: 2}))
undefined
> JSON.parse(localStorage.getItem('sth'))
{a: 1, b: 2}

세션 스토리지

웹페이지의 세션이 끝날 때 저장된 데이터가 지워짐
브라우저에서 같은 웹사이트를 여러 탭이나 창에 띄우면, 여러 개의 세션 스토리지에 데이터가 서로 격리되어 저장되며, 각 탭이나 창이 닫힐 때 저장해 둔 데이터도 함께 소멸

로컬 스토리지

웹페이지의 세션이 끝나더라도 데이터가 지워지지 않음
여러 탭이나 창 간에 데이터가 서로 공유되며 탭이나 창을 닫아도 데이터는 브라우저에 그대로 남아 있음
→ 로컬 스토리지의 데이터 영속성(persistence)은 동일한 컴퓨터에서 동일한 브라우저를 사용할 때에 한정됨

https://www.daleseo.com/js-web-storage/

프로그래머스 문제 풀기

➡️ 메뉴 리뉴얼

def solution(orders, course):
    course_combi={} # 구성 메뉴 수: 가능한 조합 수
    for c in course:
        temp=[]
        for order in orders:
            if len(order)>=c:
                order=''.join(sorted(order)) # 알파벳 순서에 따라서 조합이 분리되는 다르게 인식되는 경우 방지
                temp.extend(list(combinations(order, c)))
        course_combi[c]=temp
    
    combi_freq=[] # 조합별 빈도수
    for k in course_combi.keys():
        combi_freq.append(dict(Counter(course_combi[k])))
    
    answer = [] # 2이상이면서 max값인 경우 리턴
    for k in combi_freq:
        for kk in k.keys():
            if k[kk]==max(list(k.values())) and k[kk]>=2:
                answer.append(''.join(kk))
    answer.sort() # 알파벳 순으로 정렬
    
    return answer

코스를 구성하는 메뉴 수에 따라서 주문한 메뉴에서 combinations를 이용해서 조합해서 course_combi 딕셔너리의 밸류 값으로 넣어줬다. 이때 append를 하면 밸류 리스트 안에 order별로 리스트로 또 분리가 되어서 값을 쓰기가 힘들다. 아래 기념사진 참고
이후 만들어진 딕셔너리에서 조합 별 빈도수를 Counter를 이용해서 계산 후 딕셔너리로 바꿔서 리스트에 넣어줘서 리스트 안에 딕셔너리가 course 수만큼 들어가 있는 combi_freq를 완성했다
마지막으로 카운터로 정리한 리스트 안의 각각의 딕셔너리에서 가장 큰 값을 가진 메뉴를 뽑아야하므로 각 딕셔너리들의 밸류값 중 최댓값과 일치하는 값을 가진 키값을 answer에 append했다. 이 때 max는 조건에 따라 2이상이어야 한다
마지막으로 알파벳 순으로 정렬해주면 끝~~ 진짜 조합은 리스트가 너무 길게 나와서 토할 것 같다 ^^

#append 쓰는 경우
 {2: [[('A', 'B'), ('A', 'C'), ('A', 'F'), ('A', 'G'), ('B', 'C'), ('B', 'F'), ('B', 'G'), ('C', 'F'), ('C', 'G'), ('F', 'G')], [('A', 'C')], [('C', 'D'), ('C', 'E'), ('D', 'E')], [('A', 'C'), ('A', 'D'), ('A', 'E'), ('C', 'D'), ('C', 'E'), ('D', 'E')], [('B', 'C'), ('B', 'F'), ('B', 'G'), ('C', 'F'), ('C', 'G'), ('F', 'G')], [('A', 'C'), ('A', 'D'), ('A', 'E'), ('A', 'H'), ('C', 'D'), ('C', 'E'), ('C', 'H'), ('D', 'E'), ('D', 'H'), ('E', 'H')]], 
  3: [[('A', 'B', 'C'), ('A', 'B', 'F'), ('A', 'B', 'G'), ('A', 'C', 'F'), ('A', 'C', 'G'), ('A', 'F', 'G'), ('B', 'C', 'F'), ('B', 'C', 'G'), ('B', 'F', 'G'), ('C', 'F', 'G')], [('C', 'D', 'E')], [('A', 'C', 'D'), ('A', 'C', 'E'), ('A', 'D', 'E'), ('C', 'D', 'E')], [('B', 'C', 'F'), ('B', 'C', 'G'), ('B', 'F', 'G'), ('C', 'F', 'G')], [('A', 'C', 'D'), ('A', 'C', 'E'), ('A', 'C', 'H'), ('A', 'D', 'E'), ('A', 'D', 'H'), ('A', 'E', 'H'), ('C', 'D', 'E'), ('C', 'D', 'H'), ('C', 'E', 'H'), ('D', 'E', 'H')]], 
  4: [[('A', 'B', 'C', 'F'), ('A', 'B', 'C', 'G'), ('A', 'B', 'F', 'G'), ('A', 'C', 'F', 'G'), ('B', 'C', 'F', 'G')], [('A', 'C', 'D', 'E')], [('B', 'C', 'F', 'G')], [('A', 'C', 'D', 'E'), ('A', 'C', 'D', 'H'), ('A', 'C', 'E', 'H'), ('A', 'D', 'E', 'H'), ('C', 'D', 'E', 'H')]]}
#extend 쓰는 경우
 {2: [('A', 'B'), ('A', 'C'), ('A', 'F'), ('A', 'G'), ('B', 'C'), ('B', 'F'), ('B', 'G'), ('C', 'F'), ('C', 'G'), ('F', 'G'), ('A', 'C'), ('C', 'D'), ('C', 'E'), ('D', 'E'), ('A', 'C'), ('A', 'D'), ('A', 'E'), ('C', 'D'), ('C', 'E'), ('D', 'E'), ('B', 'C'), ('B', 'F'), ('B', 'G'), ('C', 'F'), ('C', 'G'), ('F', 'G'), ('A', 'C'), ('A', 'D'), ('A', 'E'), ('A', 'H'), ('C', 'D'), ('C', 'E'), ('C', 'H'), ('D', 'E'), ('D', 'H'), ('E', 'H')], 
  3: [('A', 'B', 'C'), ('A', 'B', 'F'), ('A', 'B', 'G'), ('A', 'C', 'F'), ('A', 'C', 'G'), ('A', 'F', 'G'), ('B', 'C', 'F'), ('B', 'C', 'G'), ('B', 'F', 'G'), ('C', 'F', 'G'), ('C', 'D', 'E'), ('A', 'C', 'D'), ('A', 'C', 'E'), ('A', 'D', 'E'), ('C', 'D', 'E'), ('B', 'C', 'F'), ('B', 'C', 'G'), ('B', 'F', 'G'), ('C', 'F', 'G'), ('A', 'C', 'D'), ('A', 'C', 'E'), ('A', 'C', 'H'), ('A', 'D', 'E'), ('A', 'D', 'H'), ('A', 'E', 'H'), ('C', 'D', 'E'), ('C', 'D', 'H'), ('C', 'E', 'H'), ('D', 'E', 'H')], 
  4: [('A', 'B', 'C', 'F'), ('A', 'B', 'C', 'G'), ('A', 'B', 'F', 'G'), ('A', 'C', 'F', 'G'), ('B', 'C', 'F', 'G'), ('A', 'C', 'D', 'E'), ('B', 'C', 'F', 'G'), ('A', 'C', 'D', 'E'), ('A', 'C', 'D', 'H'), ('A', 'C', 'E', 'H'), ('A', 'D', 'E', 'H'), ('C', 'D', 'E', 'H')]}

끝없는 리스트의 지옥을 간직하고 싶었다 기념사진임 ㅋㅅㅋ

✔️ 정리

조합별 빈도수 리스트를 만드는 부분의 for문이 위의 구성 메뉴수 별 가능한 조합수 만드는 부분이랑 똑같은 범위를 사용하고 있어서 합쳐줬다
course_combi딕셔너리도 더이상 필요하지 않게됐다

    combi_freq=[]
    for c in course:
        temp=[]
        for order in orders:
            if len(order)>=c:
                order=''.join(sorted(order))
                temp.extend(list(combinations(order, c)))
        combi_freq.append(dict(Counter(temp)))

✔️ 또 정리

다른 사람 코드랑 비교해보니까 아랫 부분도 정리할 수 있을 것 같다....
이 for문도 첫번째 for문 안에 들어가서 총 3개로 분리되어있던 for문을 하나로 합치게 됐다 ㅎㅅㅎ

combi_freq=(dict(Counter(temp)))
        for cf in combi_freq.keys():
            if combi_freq[cf]==max(list(combi_freq.values())) and combi_freq[cf]>=2:
                answer.append(''.join(cf))

완성본 🤓

from itertools import combinations
from collections import Counter

def solution(orders, course):
    # combi_freq=[]
    answer = []
    for c in course:
        temp=[]
        for order in orders:
            if len(order)>=c:
                order=''.join(sorted(order))
                temp.extend(list(combinations(order, c)))
        combi_freq=(dict(Counter(temp)))
        for cf in combi_freq.keys():
            if combi_freq[cf]==max(list(combi_freq.values())) and combi_freq[cf]>=2:
                answer.append(''.join(cf))
    answer.sort()
    return answer

combi_freq라는 리스트도 필요하지 않게 되어 삭제했다
대신 combi_freq변수에 카운터값을 넣고 각 코스에 대한 combi_freq변수의 최댓값이 answer에 append되기 때문에 결과는 계속 누적되어 저장되기때문에 사실상 마지막 for문이 굳이 필요하지 않았던 거였다.
마지막 for문을 위해서 combi_freq 라는 리스트가 필요했던 건데 없어지니까 중복되는 데이터가 없어져서 더 좋은 것 같다

데이터가 어떤 리스트에 담겨서 정리된 상태일 때 처리하기가 더 쉬운 모양이라 이제까지는 그렇게 했는데 그러면 코드의 낭비도 있고 메모리의 낭비도 있을 것 같다
어차피 append는 누적값이니 개별적으로 처리해서 append할 수 있으면 그렇게 하는 편이 더 좋은 것 같다

➡️ 조건에 맞는 도서와 저자 리스트 출력

SELECT B.BOOK_ID, A.AUTHOR_NAME, DATE_FORMAT(B.PUBLISHED_DATE,"%Y-%m-%d") AS PUBLISED_DATE 
FROM BOOK B 
JOIN AUTHOR A
ON B.AUTHOR_ID = A.AUTHOR_ID
WHERE B.CATEGORY = "경제"
ORDER BY B.PUBLISHED_DATE

날짜 형식 지정 DATE_FORMAT
AS 표시할 칼럼명

profile
looooggi

0개의 댓글