728x90

문제 설명

어떤 문장의 각 알파벳을 일정한 거리만큼 밀어서 다른 알파벳으로 바꾸는 암호화 방식을 시저 암호라고 합니다. 예를 들어 "AB"는 1만큼 밀면 "BC"가 되고, 3만큼 밀면 "DE"가 됩니다. "z"는 1만큼 밀면 "a"가 됩니다. 문자열 s와 거리 n을 입력받아 s를 n만큼 민 암호문을 만드는 함수, solution을 완성해 보세요.

제한 조건
  • 공백은 아무리 밀어도 공백입니다.
  • s는 알파벳 소문자, 대문자, 공백으로만 이루어져 있습니다.
  • s의 길이는 8000이하입니다.
  • n은 1 이상, 25이하인 자연수입니다.
입출력 예snresult
"AB" 1 "BC"
"z" 1 "a"
"a B z" 4 "e F d"

 

나의 풀이

def solution(s, n):
    lower_list = "abcdefghijklmnopqrstuvwxyz"
    upper_list = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

    result = []

    for i in s:
        if i is " ":
            result.append(" ")
        elif i.islower() is True:
            new_ = lower_list.find(i) + n
            result.append(lower_list[new_ % 26])
        else:
            new_ = upper_list.find(i) + n
            result.append(upper_list[new_ % 26])
    return "".join(result)

내장함수의 존재도 몰랐고 그냥 노가다 풀이이다..

아스키코드를 사용하면 더 편하게 구해질 것 같다.

 

다른 사람의 풀이

def solution(s, n):
    s = list(s)
    
    for i in range(len(s)):
        if s[i].isupper():
            s[i]=chr((ord(s[i])-ord('A')+ n)%26+ord('A'))
        elif s[i].islower():
            s[i]=chr((ord(s[i])-ord('a')+ n)%26+ord('a'))

    return "".join(s)
  • 각 문자열을 하나씩 쪼개서 아스키코드로 변환해야하기 때문에 list(s)로 치환해서 담았다. 
  • 만약 s[i]번째 문자가 대문자/소문자라면(isupper/islower) s[i]의 값은 (s[i]의 ASCII값 - 'A'/'a'의 ASCII값(97/65))+n을 해주어 몇 번째에 있는 알파벳인지 찾아준다. (알파벳은 총 25글자)
  • ord(s[i])-ord('A') (또는 ord('a')) 26으로 나눈 나머지 값에 'A'/'a' ASCII 값 = 97/65을 더해주면 n만큼 민 값이 나오게 된다. 
  • %26을 해주는 이유 ? z 또는 Z의 범위를 넘어가지 않도록 하기 위해
  • 즉, 맨 처음 값인 ord('A')와 ord('a')에서 n만큼 증가한 값이 무엇인지 찾는 것이다. 
  • 공백은 밀어도 공백이기 때문에 무시한다.
  • ""을 기준으로 join하여 return

 

✅ Python ASCII 

  • ord(문자) : 문자에 해당하는 ASCII 정수값 반환 
  • chr(정수) : 정수에 해당하는 ASCII 문자 반환
  • string.ascii_lowercase : 소문자에 해당하는 ASCII 값 
  • string.ascii_uppercase : 대문자에 해당하는 ASCII 값

 

✅ ASCII Chart 

문제 다가가기

(1) 아스키코드 값을 n만큼 더하여 문자로 치환한다.

(2) 단, 공백은 그대로 표현된다.

(3) 만약 문자가 Z이고, 10을 더한다면 J 가 나와야한다. 

 

>> 문자열의 아스키코드에서 A/a의 아스키 값을 뺀 후 n을 더해서 문자열이 몇번째 알파벳인지 파악해야한다. 이 값은 26으로 나눴다. 왜 26으로 나눠야하는지 몰랐는데 위에서 참고한 링크를 보니 Z/z의 범위를 넘어가지 않게 하기 위함이라고 한다. (진짜.. 어떻게 저런 생각까지 하는걸까..?)

26을 나눈 값에 다시 A/a의 아스키 값을 더해주면 n만큼 뒤로 밀린 값이 나오게 된다.

공백일 땐 for문이 진행되도록 continue를 해줬다.


코드 설명

우선 이 문제를 풀기 전 반드시 알아두어야하는 파이썬의 내장함수가 있다.

 - ord(문자) : 해당 문자의 ASCII 정수값 반환 

- chr(정수) : 해당 정수의 ASCII 문자 반환

- string.ascii_lowercase : 소문자 ASCII 값 

- string.ascii_uppercase : 대문자 ASCII 값

 

파이썬에서 아스키코드로 변환하는 방법을 찾아보다가 이 4가지 방법을 알게 되었다. 무작정 하드코딩하는 것 보다 이런 방법을 알고 있으면 훨씬 쉽고 간단하게 코드를 짤 수 있는 것 같다.. 아직 많이 모르고 있다는 걸 이번 문제를 풀면서 또 느꼈다!

 

나는 ord(), chr() 함수에 대해 설명하고자 한다.

 

(1) 문자열 탐색 및 공백 처리

def solution(s, n):
    answer = ''
    for i in s:
        if i == " ":
            answer += " "

문자열 s를 보고 문자 하나씩 탐색해나간다.

만약, i(문자)가 공백문자열이라면, 결과에 공백 문자열을 더해준다. 

 

(2) 아스키코드 처리

def solution(s, n):
    answer = ''
    for i in s:
        if i == " ":
            answer += " "
            
	else:
            k = chr(ord(i) + n)
            if k.isupper() != i.isupper() or not k.isalpha():
                k = chr(ord(k) - 26)
            answer += k
    return answer

- 만약 문자가 공백이 아닐 경우 (else문으로 들어온다.)

- 임시 변수 k에 i의 아스키 코드값을 구한 후 n을 더해주고 이를 다시 문자로 변환한다.

    -> k = 임시 변수

    -> ord(k) + n : k의 아스키 코드 값에 n을 더해줌.

    -> chr(ord(k) + n) : 다시 문자로 바꾸어줌.

 

- 임시변수 k에는 두가지 상황이 있을 수 있다. -> else 문 내에 if 문으로 처리

    (1) k가 특수문자인 경우 (대소문자의 범위에서 벗어남)

    (2) 기존 문자는 대문자이지만, n을 더한 k가 소문자가 된 경우

        -> 아스키코드 표에서 문자 = Z, n =10인 경우를 생각하면 된다.

    (처리방법)

        - 만약 현재 문자 i는 대문자인데, k가 소문자인 경우, 또는 k가 알파벳이 아닌 경우

            -> k에서 26(알파벳의 개수)만큼 빼서 해결할 수 있다.

                -> i = Z, n = 10일 때를 생각해보자.    

                -> i에 n을 더하면 k는 d가 된다. 이때  k.isalpha는 참이지만, 앞 조건이 거짓이 된다.

                -> k에서 26을 빼 주면 J가 된다.

                -> Z에서 10을 더하면  J가 된다!!!

728x90
728x90

문제 설명

다음은 아이스크림 가게의 상반기 주문 정보를 담은 FIRST_HALF 테이블과 아이스크림 성분에 대한 정보를 담은 ICECREAM_INFO 테이블입니다. FIRST_HALF 테이블 구조는 다음과 같으며, SHIPMENT_ID, FLAVOR, TOTAL_ORDER 는 각각 아이스크림 공장에서 아이스크림 가게까지의 출하 번호, 아이스크림 맛, 상반기 아이스크림 총주문량을 나타냅니다. FIRST_HALF 테이블의 기본 키는 FLAVOR입니다.

NAMETYPENULLABLE
SHIPMENT_ID INT(N) FALSE
FLAVOR VARCHAR(N) FALSE
TOTAL_ORDER INT(N) FALSE

ICECREAM_INFO 테이블 구조는 다음과 같으며, FLAVOR, INGREDITENT_TYPE 은 각각 아이스크림 맛, 아이스크림의 성분 타입을 나타냅니다. INGREDIENT_TYPE에는 아이스크림의 주 성분이 설탕이면 sugar_based라고 입력되고, 아이스크림의 주 성분이 과일이면 fruit_based라고 입력됩니다. ICECREAM_INFO의 기본 키는 FLAVOR입니다. ICECREAM_INFO테이블의 FLAVOR는 FIRST_HALF 테이블의 FLAVOR의 외래 키입니다.

NAMETYPENULLABLE
FLAVOR VARCHAR(N) FALSE
INGREDIENT_TYPE VARCHAR(N) FALSE

문제

상반기 아이스크림 총주문량이 3,000보다 높으면서 아이스크림의 주 성분이 과일인 아이스크림의 맛을 총주문량이 큰 순서대로 조회하는 SQL 문을 작성해주세요.


예시

예를 들어 FIRST_HALF 테이블이 다음과 같고

SHIPMENT_IDFLAVORTOTAL_ORDER
101 chocolate 3200
102 vanilla 2800
103 mint_chocolate 1700
104 caramel 2600
105 white_chocolate 3100
106 peach 2450
107 watermelon 2150
108 mango 2900
109 strawberry 3100
110 melon 3150
111 orange 2900
112 pineapple 2900

ICECREAM_INFO 테이블이 다음과 같다면

FLAVORINGREDIENT_TYPE
chocolate sugar_based
vanilla sugar_based
mint_chocolate sugar_based
caramel sugar_based
white_chocolate sugar_based
peach fruit_based
watermelon fruit_based
mango fruit_based
strawberry fruit_based
melon fruit_based
orange fruit_based
pineapple fruit_based

상반기 아이스크림 총주문량이 3,000보다 높은 아이스크림 맛은 chocolate, strawberry, melon, white_chocolate입니다. 이 중에 아이스크림의 주 성분이 과일인 아이스크림 맛은 strawberry와 melon이고 총주문량이 큰 순서대로 아이스크림 맛을 조회하면 melon, strawberry 순으로 조회되어야 합니다. 따라서 SQL 문을 실행하면 다음과 같이 나와야 합니다.

FLAVOR
melon
strawberry

 

 

풀이

SELECT FIRST_HALF.FLAVOR 
FROM FIRST_HALF JOIN ICECREAM_INFO 
ON FIRST_HALF.FLAVOR = ICECREAM_INFO.FLAVOR
WHERE TOTAL_ORDER > 3000 AND INGREDIENT_TYPE = "fruit_based"
ORDER BY TOTAL_ORDER DESC
SELECT a.FLAVOR 
FROM FIRST_HALF AS a 
LEFT JOIN ICECREAM_INFO AS b 
ON a.FLAVOR = b.FLAVOR
WHERE a.TOTAL_ORDER > 3000 AND b.INGREDIENT_TYPE LIKE 'fruit_based'
ORDER BY a.TOTAL_ORDER DESC;

-- FLAVOR는 공통 컬럼, 테이블 명 작성 필수

 

정리

JOIN
데이터베이스 내의 '여러 테이블'에서 가져온 레코드를 조합하여 '하나의 테이블'이나 '결과 집합'으로 표현해 주는, Relation Database 에서 가장 많이 쓰이는 녀석이다.

(INNER) JOIN
여기서 사용된 조인은 가장 기본적인 조인이다.

  • 조인하는 테이블의 ON 절의 조건이 일치하는 결과만 출력
  • 표준 SQL과는 달리 MySQL에서는 JOIN, INNER JOIN, CROSS JOIN이 모두 같은 의미로 사용된다.
SELECT u.userid, name 
FROM usertbl as u INNER JOIN buytbl as b 
ON u.userid=b.userid 
WHERE u.userid="111" -- join을 완료하고 그다음 조건을 따진다.

함축 구문도 존재한다. (콤마)

SELECT u.userid, name 
FROM usertbl u, buytbl b 
ON u.userid=b.userid 
WHERE u.userid="111" -- join을 완료하고 그다음 조건을 따진다.

 

https://inpa.tistory.com/entry/MYSQL-%F0%9F%93%9A-JOIN-%EC%A1%B0%EC%9D%B8-%EA%B7%B8%EB%A6%BC%EC%9C%BC%EB%A1%9C-%EC%95%8C%EA%B8%B0%EC%89%BD%EA%B2%8C-%EC%A0%95%EB%A6%AC

728x90
728x90

파이썬에서 리스트 형태에 저장된 값을 삭제하는 방법을 보겠습니다.

리스트에서 값을 삭제하기 위해서는 함수를 사용하거나 범위를 지정해 삭제할 수 있습니다.

 

 종류  내용
 clear()  모든 요소 삭제
 pop()  지정한 위치 값을 삭제하고 삭제한 값 취득
 remove()  지정한 위치 값과 같은 값을 검색후 처음 값을 삭제
 del  위치 또는 범위를 지정 삭제

 

  clear()

리스트에 저장된 모든 값을 삭제합니다.

l = list(range(10))
print(l)
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

l.clear()
print(l)
# []

 

  pop()

지정한 위치에 값을 취득한 후 삭제합니다.

l = list(range(10))
print(l)
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

print(l.pop(0))
# 0

print(l)
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

print(l.pop(3))
# 4

print(l)
# [1, 2, 3, 5, 6, 7, 8, 9]

 

리스트 인덱스는 0부터 시작합니다.

뒤에서부터 인덱스를 지정하고 싶은 경우는 마이너스를 사용합니다.

뒤에서부터 위치를 지정하는 경우는 -1부터 시작합니다.

l = [1, 2, 3, 5, 6, 7, 8, 9]

print(l.pop(-2))
# 8

print(l)
# [1, 2, 3, 5, 6, 7, 9]

 

인덱스를 생략한 경우에는 마지막 값을 취득하고 삭제합니다.

l = [1, 2, 3, 5, 6, 7, 9]

print(l.pop())
# 9

print(l)
# [1, 2, 3, 5, 6, 7]

 

존재하지 않는 인덱스를 지정한 경우에는 에러가 발생합니다.

print(l.pop(100))
IndexError: pop index out of range
 

  remove()

설정한 값과 같은 요소를 검색하고 값을 삭제합니다.

설정한 값이 리스트에 여러개 있는 경우에는 가장 앞에 있는 값 하나만 삭제합니다.

l = ['Alice', 'Bob', 'Charlie', 'Bob', 'Dave']
print(l)
# ['Alice', 'Bob', 'Charlie', 'Bob', 'Dave']

l.remove('Alice')
print(l)
# ['Bob', 'Charlie', 'Bob', 'Dave']

l.remove('Bob')
print(l)
# ['Charlie', 'Bob', 'Dave']

 

설정한 값이 존재하지 않는 경우에는 에러가 발생합니다.

l.remove('xxx')
ValueError: list.remove(x): x not in list

 

  del

삭제하고 싶은 값을 인덱스로 지정합니다.

l = list(range(10))
print(l)
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

del l[0]
print(l)
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

del l[-1]
print(l)
# [1, 2, 3, 4, 5, 6, 7, 8]

del l[6]
print(l)
# [1, 2, 3, 4, 5, 6, 8]

 

여러개 값을 지우고 싶은 경우에는 슬라이스로 범위를 지정합니다.

l = list(range(10))
print(l)
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

del l[2:5]
print(l)
# [0, 1, 5, 6, 7, 8, 9]

l = list(range(10))
del l[:3]
print(l)
# [3, 4, 5, 6, 7, 8, 9]

l = list(range(10))
del l[4:]
print(l)
# [0, 1, 2, 3]

l = list(range(10))
del l[-3:]
print(l)
# [0, 1, 2, 3, 4, 5, 6]

 

728x90
728x90

문제 설명

S사에서는 각 부서에 필요한 물품을 지원해 주기 위해 부서별로 물품을 구매하는데 필요한 금액을 조사했습니다. 그러나, 전체 예산이 정해져 있기 때문에 모든 부서의 물품을 구매해 줄 수는 없습니다. 그래서 최대한 많은 부서의 물품을 구매해 줄 수 있도록 하려고 합니다.

물품을 구매해 줄 때는 각 부서가 신청한 금액만큼을 모두 지원해 줘야 합니다. 예를 들어 1,000원을 신청한 부서에는 정확히 1,000원을 지원해야 하며, 1,000원보다 적은 금액을 지원해 줄 수는 없습니다.

부서별로 신청한 금액이 들어있는 배열 d와 예산 budget이 매개변수로 주어질 때, 최대 몇 개의 부서에 물품을 지원할 수 있는지 return 하도록 solution 함수를 완성해주세요.

제한사항
  • d는 부서별로 신청한 금액이 들어있는 배열이며, 길이(전체 부서의 개수)는 1 이상 100 이하입니다.
  • d의 각 원소는 부서별로 신청한 금액을 나타내며, 부서별 신청 금액은 1 이상 100,000 이하의 자연수입니다.
  • budget은 예산을 나타내며, 1 이상 10,000,000 이하의 자연수입니다.

입출력 예dbudgetresult
[1,3,2,5,4] 9 3
[2,2,3,3] 10 4
입출력 예 설명

입출력 예 #1
각 부서에서 [1원, 3원, 2원, 5원, 4원]만큼의 금액을 신청했습니다. 만약에, 1원, 2원, 4원을 신청한 부서의 물품을 구매해주면 예산 9원에서 7원이 소비되어 2원이 남습니다. 항상 정확히 신청한 금액만큼 지원해 줘야 하므로 남은 2원으로 나머지 부서를 지원해 주지 않습니다. 위 방법 외에 3개 부서를 지원해 줄 방법들은 다음과 같습니다.

  • 1원, 2원, 3원을 신청한 부서의 물품을 구매해주려면 6원이 필요합니다.
  • 1원, 2원, 5원을 신청한 부서의 물품을 구매해주려면 8원이 필요합니다.
  • 1원, 3원, 4원을 신청한 부서의 물품을 구매해주려면 8원이 필요합니다.
  • 1원, 3원, 5원을 신청한 부서의 물품을 구매해주려면 9원이 필요합니다.

3개 부서보다 더 많은 부서의 물품을 구매해 줄 수는 없으므로 최대 3개 부서의 물품을 구매해 줄 수 있습니다.

입출력 예 #2
모든 부서의 물품을 구매해주면 10원이 됩니다. 따라서 최대 4개 부서의 물품을 구매해 줄 수 있습니다.

 

나의 풀이

def solution(d, budget):
    answer = 0    
    d.sort()
    now=0
    for x in d:
        if (now+x)<=budget:
            now+=x
            answer+=1
        else:
            break
    return answer

 

 

다른 사람의 풀이

def solution(d, budget):
    d.sort()
    while budget < sum(d):
        d.pop()
    return len(d)
728x90
728x90

문자열(string)을 리스트(list)로


입력된 문자열 "I love python"을 단어 단위로 list로 변환하려면

a= "I love python"
print(a.split()) #공백을 기준으로 나눈다.

>>['I','love','python']

 

split()의 인자로 값을 주면 그 값을 기준으로 나눈다.

a='I/love/python'
print(a.split('/'))

>>['I', 'love', 'python']

단어 단위가 아닌 알파벳 하나씩 나누고싶다면,

a="I love python"
print(list(a))

>>['I', ' ', 'l', 'o', 'v', 'e', ' ', 'p', 'y', 't', 'h', 'o', 'n']

 

 

리스트(list)를 문자열(string)로


위와 반대로 리스트를 문자열로 바꿀수도 있다.

 

a=['I','love','python']
print("".join(a)) # 각 요소를 공백없이 붙인다.
print(" ".join(a)) # 요소사이에 공백을 추가.
print("\n".join(a)) # 한줄에 하나씩.

>>Ilovepython
>>I love python
>>I
>>love
>>python

※리스트의 구성요소가 모두 문자열이어야 가능!

728x90
728x90

문제 설명

문자열 s는 한 개 이상의 단어로 구성되어 있습니다. 각 단어는 하나 이상의 공백문자로 구분되어 있습니다. 각 단어의 짝수번째 알파벳은 대문자로, 홀수번째 알파벳은 소문자로 바꾼 문자열을 리턴하는 함수, solution을 완성하세요.

제한 사항
  • 문자열 전체의 짝/홀수 인덱스가 아니라, 단어(공백을 기준)별로 짝/홀수 인덱스를 판단해야합니다.
  • 첫 번째 글자는 0번째 인덱스로 보아 짝수번째 알파벳으로 처리해야 합니다.
입출력 예sreturn
"try hello world" "TrY HeLlO WoRlD"
입출력 예 설명

"try hello world"는 세 단어 "try", "hello", "world"로 구성되어 있습니다. 각 단어의 짝수번째 문자를 대문자로, 홀수번째 문자를 소문자로 바꾸면 "TrY", "HeLlO", "WoRlD"입니다. 따라서 "TrY HeLlO WoRlD" 를 리턴합니다.

<풀이>

def solution(s):
    
    ss = s.split(" ")
    
    answer = ''
    
    for i in range(len(ss)):
        for j in range(len(ss[i])):
            if j % 2 == 0: 
                answer += ss[i][j].upper()
            else:
                answer += ss[i][j].lower()
        
        if i != len(ss) - 1:
            answer += ' '
    
    return answer
def solution(s):
    s_split = s.split(" ")  # 받은 문자열을 단어별(스페이스 기준)으로 split해서 list에 넣어주기
    
    for k in range(len(s_split)):  # 단어 리스트에 있는 단어들 반복할 때 그 단어 길이 만큼 반복문
        s_list = list(s_split[k]) # 문자열을 새로 만들어야 하니 s_list라는 리스트에 입력

        for i in range(len(s_list)):  # 단어 리스트에 있는 단어들 반복할 때 그 단어 길이 만큼 반복문
            if i % 2 == 0:
                s_list[i] = s_list[i].upper() # 단어가 홀수면 대문자로 바꿔서 리스트에 붙여주기
            elif i % 2 == 1:
                s_list[i] = s_list[i].lower() # 단어가 짝수면 소문자로 바꿔서 리스트에 붙여주기
        s_split[k] = "".join(s_list)
        
    answer = " ".join(s_split)  # 새로 채워진 list s_split에 있는 요소들 붙여서 return (스페이스 중간에 넣어주기)

    
    return answer

1. 공백(" ")을 기준으로 문자열을 나누기

2. 문자열을 받아서 인덱스가 짝수면 대문자로, 홀수면 소문자로 문자열 지정 => 2로 나눈 나머지가 0이면 짝수로 대문자, 아니면 홀수로 소문자

2-1) 대문자로 만들기 위해 .upper() 함수 사용

2-2) 소문자로 만들기 위해 .lower() 함수 사용

3. 한 문자열의 판단이 끝나면 공백을 추가

4. 결과의 마지막 공백은 제거후 리턴

[TrY, HeLlO, WoRlD]가 된다. 여기서 (" ").join으로 문자열을 합쳐서 반환하자.

728x90
728x90

JSP & Servlet을 활용한 WEB 개발에 대해서 전체적으로 학습을 진행하는 와중,  Redirect와 foward 개념에 대해서 정리해보고자 한다. 또한 간단한 코드를 통해서 동작 방식이 어떻게 다른지 역시 알아보고자 한다.

 

1. Forward 방식


[ Forward 방식 ]

Forward 방식을 적절한 비유를 통해서 간단하게나마 이해해보자. 

고객을 클라이언트, 상담원을 서버, URL을 통해서 서버의 자원에 접근한다고 생각하시면 된다.

 

 

  1. 고객이 고객센터로 상담원에게 100번으로 전화를 건다.
  2. 상담원은 해당문의 사항에 대해서 전문적인 지식을 갖춘 상담원에게 문의해 답을 얻는다.
  3. 상담원은 고객에게 문의사항을 처리해준다.

 

Forward는 Web Container 차원에서 페이지의 이동만 존재한다. 실제로 웹 브라우저는 다른 페이지로 이동했음을 알 수 없다. 그렇기 때문에 웹 브라우저에는 최초에 호출한 URL이 표시되고, 이동한 페이지의 URL 정보는 확인할 수 없다. 또한 현재 실행중인 페이지와 forward에 의해 호출될 페이지는 Request 객체와 Response 객체를 공유한다. 

 

위와 같이 Foward는 다음으로 이동 할 URL로 요청정보를 그대로 전달한다. 그렇기 때문에 사용자가 최초로 요청한 요청정보는 다음 URL에서도 유효하다. 예를 들어 게시판을 작성하는 과정이라고 할 때, 사용자가 보낸 요청 정보를 이용하여 글쓰기 기능을 수행한다고 할 때, forward를 사용하여 응답 페이지를 부르면 다음과 같은 문제가 발생하게 된다. 만약 사용자가 실수 혹은 고의로 글쓰기 응답 페이지에서 새로고침을 누른다면, 요청 정보가 그대로 살아있기 때문에 요청이 여러 번 전달되어 동일한 게시물이 여러 번 등록될 수 있다. 그렇기 때문에 게시판을 제작하는 과정에서는 시스템에 변화가 생기지 않는 단순 조회 요청(글 목록 보기, 검색)의 경우 forward로 응답하는 것이 바람직하다.

package controller;
 
import java.io.IOException;
 
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import model.MockDAO;
 
@WebServlet("/ForwardTestServlet")
public class ForwardTestServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
       protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //Model과 연동 
           MockDAO dao=new MockDAO();
           dao.register("상품정보");
           //forward 방식으로 이동 
           String path="forward-result.jsp";
           //forward 방식은 기존 request와 response 가 유지된 상태로 이동된다 
           //아래와 같이 request에 정보를 할당할 수 있다 
           request.setAttribute("shareInfo", "검색정보");
           request.getRequestDispatcher(path).forward(request, response);
    }
}

 

 

 

 

2. Redirect 방식


[ Redirect 방식 ]

Redirect를 잘 설명해주는 적절한 비유로 이해를 해보자.

고객을 클라이언트, 상담원을 서버, URL을 통해서 서버의 자원에 접근한다고 생각하시면 된다.

 

  1. 고객이 고객센터로 상담원에게 100번으로 전화를 건다. 
  2. 상담원은 고객에게 다음과 같이 이야기한다. "고객님 해당 문의 사항은 200번으로 다시 문의해주세요"
  3. 고객은 다시 200번으로 문의해 일을 처리한다.

Redirect는 Web Container로 명령이 들어오면, 웹 브라우저에게 다른 페이지로 이동하라고 명령을 내린다. 그러면 웹 브라우저는 URL을 지시된 주소로 바꾸고 해당 주소로 이동한다. 다른 웹 컨테이너에 있는 주소로 이동하며 새로운 페이지에서는 Request와 Response객체가 새롭게 생성된다.

 

Redirect의 경우 최초 요청을 받은 URL1에서 클라이언트에게 redirect할 URL2를 반환하고, 클라이언트에서는 새로운 요청을 생성하여 URL2에 다시 요청을 보낸다. 그러므로 처음 보냈던 최초의 Request와 Response 객체는 유효하지 않고 새롭게 생성되는 것이다. 예를 들어 게시판을 작성하는 과정이라고 할 때, 사용자가 보낸 요청 정보를 이용하여 글쓰기 기능을 수행한다고 할 때, redirect를 사용하여 응답 페이지를 부르면 사용자가 실수 혹은 고의로 글쓰기 응답 페이지에서 새로고침을 누른다고 하더라도, 처음의 요청 정보는 존재하지 않으므로 게시물이 여러 번 등록되지 않는다. 그렇기 때문에시스템에 변화가 생기는 요청(회원가입, 글쓰기 등)의 경우에는 redirection을 사용하는 것이 바랍직하다.

 

package controller;
 
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import model.MockDAO;
 
 
@WebServlet("/RedirectTestServlet")
public class RedirectTestServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;           
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //모델 연동 
        MockDAO dao=new MockDAO();
        dao.register("상품정보");
        //redirect 방식으로 이동 
        //redirect 방식은 응답시에 클라이언트에게 이동할 url 을 지정해서 클라이언트가 view로 요청하게 하는 방식으로
        //기존 request와 response는 유지되지 않는다 
        //아래 코드와 같이 정보를 공유할 수는 없다 
        request.setAttribute("shareInfo", "검색정보");
        response.sendRedirect("redirect-result.jsp");
    }
}

 

3. 정리


 

우선 Redirect의 경우 URL에 변화가 생긴다. 처음에는 /RedirectTestServlet으로 URL 요청을 하였다면 응답으로 받은 경로인 /redirect-result.jsp로 요청을 다시 보낼 것이다. 따라서 새로운 request, response가 생성이 된다.

 

반면에 Forward의 경우 /ForwardTestServlet으로 URL 요청을 하였다면 서버 내부적으로 Controller가 View에게 작업을 넘기고 View가 JSP 파일을 렌더링하여 결과 페이지로써 클라이언트에게 넘긴다. 이는 클라이언트가 알지 못한다. 따라서 URL 요청이 바뀌는것이 없다. 

 

결론!

DB에 변화를 주는 작업 (회원가입, 게시판 글쓰기등)의 경우 Redirect 방식으로 사용하고,

시스템에 변화가 생기지 않는 작업 (단순 조회, 검색)의 경우 forward 방식이 적합하다.

728x90

'Spring' 카테고리의 다른 글

HttpMessageConverter란?  (0) 2023.02.23
[Spring] Thymeleaf 와 jsp 비교  (0) 2023.02.05
[JAVA] JDBC란?  (0) 2023.01.29
Spring과 SpringBoot 의 정의와 사용 이유  (0) 2023.01.29
스프링이란?  (1) 2023.01.05
728x90

Pyhton 진법 변환

 

n진수 → 10진수

* 결과값은 모두 string

* python에서는 기본적으로 int() 라는 함수를 지원한다

>> int(string, base)

위와 같은 형식으로 사용. base에는 진법을 넣으면 된다.

print(int('101',2))
print(int('202',3))
print(int('303',4))
print(int('404',5))
print(int('505',6))
print(int('ACF',16))

20
51
104
185
2767

10진수로 변경이 가능하다.

10진수 → 2, 8, 16진수

2, 8, 16진수는 bin(), oct(), hex() 함수를 지원한다.

print(bin(11))
print(oct(11))
print(hex(11))

0b1011
0o13
0xb

 

0b는 2진수, 0o는 8진수, 0x는 16진수를 의미한다.

진법 표시를 지울려면 [2:]를 하면 된다.

print(bin(11)[2:])
print(oct(11)[2:])
print(hex(11)[2:])

1011
13
b


10진수 → n진수

int 같은 함수가 없기 때문에 코드작성이 필요하다

def solution(n, q):
    rev_base = ''

    while n > 0:
        n, mod = divmod(n, q) n을 p으로 나눈 몫과 나머지
        rev_base += str(mod)

    return rev_base[::-1] 
    # 역순인 진수를 뒤집어 줘야 원래 변환 하고자하는 base가 출력
    
print(solution(45, 3))

# divmod() : 몫과 나머지를 리턴. 리턴 값이 2개이므로 튜플을 사용.
# int(x, base) : base 진법으로 구성된 str 형식의 수를 10진법으로 변환해 줌

1200


n진수 → n진수

위에 썻던것들을 활용해서 n진수를 10진수로 변경하고 다시 n진수로 변경하면 된다.

print(solution(int('c',16),4)) # 16진수인 C를 4진수로 바꾸는것
print(solution(int('4',6),3))  # 6진수인 4를 3진수로 바꾸는것
print(solution(int('21',3),7)) # 3진수인 21을 7진수로 바꾸는것
print(solution(int('15',9),5)) # 9진수인 15를 5진수로 바꾸는것

30
11
10
24

728x90

+ Recent posts