[Selenium] Selenium 을 이용하여 복권 구매 자동화 만들어보기 #6

Selenium 을 사용하여 복권 구매 자동화하기는 마무리 되었습니다. 1편  - selenium 사용할 수 있는 환경 구성하고 브라우저 열기 2편  - 불필요한 팝업 닫기 3편  - 로그인 하기 4편  - 복권 구매 하기 5편  - 코드 리펙토링 우선 복권 구매 자동화의 목적은 selenium 기초를 다루기 위함이였습니다. 다룬 개념을 보자면 find~ 를 통해 element 찾기,  클릭하기, handler 개념을 통해 팝업 닫기, iframe 환경에서 조작하기, js script 실행하기 등이 있습니다. 이 글은 selenium 기초를 다루었기 때문에 조금 더 코드를 구조적으로 리펙토링할 수 있지만 하지 않았고 로그, 예외 처리 등 중요한 내용을 다루지 않았습니다. 그러므로 해당 글은 개념으로 참고해주시면 감사하겠습니다. 총 5개의 글로 복권 구매 자동화가 마무리 되었습니다. 블로그에 처음 글을 쓴지는 꽤 되었지만 이렇게 비록 5개의 글이지만 시리즈로 글을 써본 것은 처음입니다. 글을 쓴다는 것이 간단한 내용이더라도 시간이 굉장히 많이 드는 작업이다보니 항상 미루고 하였습니다. 그렇지만 그 시간만큼 저에게 남는 것이라는 것이라고 생각이 되어 짧게나마 연재 형식으로 적어보았습니다. 점점 규모를 키워가며 연재를 해보도록 하겠습니다. 

[Selenium] Selenium 을 이용하여 복권 구매 자동화 만들어보기 #5

 이번 포스팅은 이전 글 에서 작성한 코드를 리펙토링해보려고 합니다. 리펙토링이라고 하기에는 사실 거창하기는 하지만 따라하시기 편하도록 한 개의 파일 안에서 코드 작성을 이어가고 있었습니다. 코드를 작성할 때는 당연히 이해가 가지만 조금만 시간이 지나도 이게 어떤 기능을 의미하는지 잊어버릴 때가 있습니다. 이를 방지하기 위해서는 함수로 만들어 함수 이름으로 유추하게 하던지 아니면 주석을 달면 됩니다. 때에 따라서 정답은 나뉘겠지만 오늘은 함수로 기능을 묶어 맨 하단에 함수 이름을 통해 어떠한 기능을 수행하는지 알 수 있게되면 성공입니다. 위에서 언급했듯이 아래 코드는 클릭을 하기는 하는데 저 xpath 가 무엇을 의미하는지 알기가 쉽지 않습니다. click(driver.find_element(By.XPATH, '/html/body/div[1]/header/div[2]/div[2]/form/div/ul/li[1]/a')) 그렇지만 함수로 묶어 아래로 표현하면 알 수가 있게 됩니다. def move_to_login_page():      click(driver.find_element(By.XPATH, '/html/body/div[1]/header/div[2]/div[2]/form/div/ul/li[1]/a')) 일단 로그인 관련 부분을 수정해보겠습니다. 기존의 코드는 아래와 같이 나열되어 있습니다. 이 경우 사실 어떤 행위를 하는지 나중에 봐도 알 수는 있겠지만 3줄이 연달아 있는 것이 불편합니다. 이를 함수로 묶어 두고 사용하면 훨씬 코드를 작성할 때 가독성이 좋게 될 것 입니다. 팝업 닫는 코드까지 추가하였습니다. 이는 로그인 단계에서 처리하는 것이 맞다고 생각하기 때문입니다.    기존 코드 send_keys(driver.find_element(By.ID, 'userId'), os.environ.get('ID')) send_keys(driver.find_element(By.NAME, 'pass

[Selenium] Selenium 을 이용하여 복권 구매 자동화 만들어보기 #4

이미지
 이전 글 에서 로그인 기능을 만들어 보았습니다. 이번 글에서는 복권 구매까지 진행을 해보겠습니다. 다음글에서는 마지막 코드 리펙토링을 진행하도록 하겠습니다.  이 글을 끝에 구매 버튼이 있는데 진짜로 구매하시려면 예치금을 충전하시면 가능합니다. 복권 구매는 단계를 나누어 보면 아래와 같습니다. 1. 메인 페이지에서 복권 구매 버튼 클릭(클릭 시 팝업 열림) 2. 복권 구매 번호 랜덤으로 발행(자동번호 발행 기능이 이미 있지만 코드 예제를 위해 수동으로 진행) 3. 구매 버튼 클릭 단계를 나누어보면 새로 배울 개념이 없지만 여기서 문제라고하면 복권 구매 화면이 iframe 형식으로 구성이 되어있다는 것입니다. iframe 으로 되어있는 경우 selenium 을 통해 현재 frame 을 이동시켜줘야 원하는 element 등에 접근 가능합니다.  우선 몇 개의 함수를 먼저 작성하겠습니다. script 실행하기 def execute_script(script):      driver.execute_script(script) 윈도우 중에서 마지막에 열린 윈도우로 현재 윈도우 변경하기 def switch_to_last_opened_window():      driver.switch_to.window(driver.window_handles[-1]) iframe 으로 현재 프레임 변경하기 def switch_to_iframe(iframe):      WebDriverWait(driver, 10).until(           EC.presence_of_element_located((By.TAG_NAME, "iframe"))      )      driver.switch_to.frame(iframe) WebDriverWait(driver, 10).until(         EC.presence_of_element_located((By.TAG_NAME, "iframe"))     ) 의 코드가 필요한 이유는 iframe 이 나중에 로

[GIT] 간단한 코드 수정으로 mongoose 에 contributor 되기

이미지
처음으로 expo 오픈소스 기여자가 되고나니 다른 오픈소스를 사용하더라도 더 신경써서 보게되는거 같습니다.  저는 대부분의 경우 nosql 을 사용하는 것을 선호하는 편입니다.  그러다보니 mongodb 를 주로 사용합니다. node js 에서 mongodb 를 사용하기 위해서는 mongoose 라는 라이브러리를 사용하는 것이 보편적입니다. 저는 mongoose 를 사용하기 위해 공식 문서를 보다 보니 예를 들어 const foo = new Bar 이런 형식으로 되어있는 것을 보았습니다. 여기서 이상하다고 느낀 부분은  new Bar() 가 아니라 괄호가 생략된 new Bar 라는 점이였습니다. 저는 이 문법이 오류가 발생한다고 생각해서 바로 예제 코드를 작성해보니 정상적으로 돌아가서 찾아보니 인자가 없는 경우에는 괄호를 생략해도 된다는 것을 알게 되었습니다. 그렇지만 prettier 에서는 기본적으로 해당 문법을 사용하지 않도록 설정이 되어있습니다. 다시 말해 웬만하면 사용 안하는 것이 바람직하다는 것입니다. 또한 다른 mongoose 의 예제를 볼 때 빈 괄호를 사용하는 경우가 대부분이라는 것을 확인하였습니다. 제가 본 문서에서만 괄호를 추가하기보다 전체적으로 한 번 살펴보고 다 고쳐서 PR 을 올리면 더 좋겠다는 생각이 들었습니다. 그래서 new 라는 키워드로 살펴보았더니 너무 많아서 eslint 에 속성 값을 추가하여 에러 잡히는 부분을 모두 수정하였습니다. 그래서 총 310 군데를 발견하여 수정하였습니다. 커밋 후 PR 을 날렸는데 빠른 시일내에 답을 해주었습니다. 이번에도 정말 정말 작은 코드 수정으로 운이 좋게도 contirbutor 가 되었습니다. 막상 이렇게 되니 나중엔 기능도 추가하고 싶은 욕심도 듭니다. 

[자격증] 네트워크관리사 2급 취득기

이미지
네트워크관리사 자격증 은 1급, 2급으로 분류되어있습니다. 1급은 민간 자격증이며 2급은 공인민간자격증으로 되어있습니다. 저의 경우 학점은행제를 통한 학점 취득을 목적으로 땄기 때문에 2급을 취득하였으며 또한 이 자격증이 취업에 도움이 되는지는 모르겠습니다.  네트워크관리사 자격증은 필기, 실기로 구분되어 각각 시험을 응시하여야합니다. 네트워크관리사 2급의 응시료는 필기 43000원 실기는 78000원이였습니다. 꽤나 가격이 있다고 느껴질 수 있지만 저의 경우 14학점으로 인정이 되기 때문에 혜자?! 라고 느껴졌습니다.  저는 자격증 공부할 때 책이 없으면 좀 허전하게 느끼는 편이라 책을 구매해서 공부했습니다. 아래 책 교보문고 링크 입니다. 개인적으로 해당 책으로 필기 취득은 도움이 많이 되었습니다. 필기에 필요한 내용도 잘 나와있으며 가장 도움이 된 부분 아무래도 기출 문제입니다. 개인적인 느낌으로는 필기에 필요한 내용을 읽고 공부하기 보다 기출문제를 보며 공부만 해도 충분할 것 같습니다. 문제마다 아래 설명이 있기 때문에 설명을 익혀도 충분히 필기 취득은 가능하지 않을까 싶습니다. 그렇지만 저의 경우는 그래도 한 번 슥 보자 싶어 읽고 넘어가기는 했습니다.  결론적으로 기출문제를 보며 공부하는 것이 도움이 많이 되었습니다. 1급, 2급으로 나누어져 기출문제가 있지만 2급을 응시 한다고해서 1급을 안 보기보다 1급을 풀면 공통된 부분과 더 깊은 지식을 요구하는 부분이 있기 때문에 도움이 많이 됩니다. 그래서 1급, 2급 모든 기출문제를 풀어보시면 도움이 많이 되지않을까 싶습니다. 실기의 경우는 조금 막막했었던 것 같습니다. 이유는 해당 책으로는 실기를 대비할 수 없기 떄문입니다. 너무나 적은 예제를 다루고 있기 때문에 또한 설명 또한 부족하기 때문에 책을 거의 참고 하지 않았습니다. 실기는 대부분 공통일 텐데 햄릿슈 유튜브 를 참고하여 공부를 많이 하였습니다. 동영상 목록을 보면 각 년도 별로 문제 풀이 영상이 있습니다. 저는 3개년을 다 해보았더니 슬슬

[Selenium] Selenium 을 이용하여 복권 구매 자동화 만들어보기 #3

이미지
 오늘은 동행복권 사이트 로그인 기능을 만들어 보려합니다. 로그인 과정에 필요한 기능은 간단하게 아래와 같습니다. 1. 버튼 클릭 기능 2. input 에 텍스트 입력 기능 이전 단계에서 팝업 닫고 메인 페이지로 돌아오도록 하였습니다. 로그인 단계를 selenium 단계로 보면 아래와 같습니다.  1. 메인페이지에서 로그인 버튼 찾기 2. 로그인 버튼 클릭 3. 아이디 input 찾기 4. 아이디 입력 5. 비밀번호 input 찾기 6. 비밀번호 입력 7. 엔터키 입력 위 작업은 간단하기 때문에 사전에 필요한 작업을 먼저 하겠습니다. python-dotenv 설치 pip install python-dotenv python-dotenv 의 역할은 .env 파일을 읽어올 수 있게 합니다. 클릭 함수 작성 def click(element):      element.click() 위 함수는 보이시는대로 element 를 넘겨주면 클릭하는 함수입니다. 문자 입력 함수 작성 def send_keys(element, text):      element.send_keys(text) 위 코드도 보이시는대로 문자를 입력하는 함수입니다. 처음 보면 생소할 수 있지만 selenium 에서는 send_keys 를 사용하여 문자를 입력합니다. 환경 변수 읽어오기 from dotenv import load_dotenv import os load_dotenv() 상단에 위 코드를 넣어줍니다. 그러면 아래 코드처럼 환경 변수 파일인 .env 파일을 불러올 수 있습니다. os.environ.get('ID')   .env 파일을 만들어서 관리하는 이유는 이 프로젝트가 private repository 이면 상관이 크게 없을 수 있지만 public 하면 .env 로 계정 정보를 나만 알 수 있도록 관리하는 것이 필요합니다. 또한 때에 따라 다를 수 있지만 .env 에 중요한 계정 정보나 키를 넣어 놓게 되면 관리에 유용하기에 환경 변수 파일을 자주 생성해서 관리

[Selenium] Selenium 을 이용하여 복권 구매 자동화 만들어보기 #2

이미지
 오늘은 이전 글에 이어서 팝업 닫기를 해보려합니다. 이 기능을 만들 때는 상황에 따라서 여러 방법이 가능합니다.  1. 메인 URL 을 가지고 메인 페이지만 남기기 2. 팝업 URL 을 가지고 메인 페이지만 남기기 위 1, 2 번 뿐만 아니라 더 많은 접근 방법이 가능하지만 이번 글에서는 1 번에 해당하는 방식을 사용해보려 합니다. 동행복권 홈페이지 들어가보면 메인페이지와 팝업 창이 나타납니다. 이 팝업은 일시적이겠지만 꼭 팝업을 다뤄야하기 때문에 바로 다루어 보려고합니다. 설명절 관련 팝업이기 때문에 대부분의 경우는 팝업이 없을 것이라고 생각이 듭니다. 그런 경우는 어렵지 않으니 가볍게 아래 코드를 보고 넘어가도 될 것 같습니다. 1. 메인 페이지와 팝업 페이지의 핸들러 확인하기 핸들러라고 하면 각 페이지별로 고유한 ID 를 말한다고 볼 수 있습니다. 페이지별 ID 를 가지고 화면 전환 등을 할 수 있습니다. 설명이 와닿지 않을 수 있지만 오늘 예제와 나중에 다룰 부분에 다루기 때문에 금방 개념을 알 수 있을 것입니다. from selenium import webdriver from selenium.webdriver.chrome.service import Service def create_driver(): return webdriver.Chrome(service=Service('./driver/chromedriver_mac64_m1_97')) driver = create_driver() driver.get('https://dhlottery.co.kr/common.do?method=main') def get_main_handler(): for window_handle in driver.window_handles: driver.switch_to.window(window_handle) print(f'현재 핸들러 name : {driver.current_window_h

[Selenium] Selenium 을 이용하여 복권 구매 자동화 만들어보기 #1

이미지
저는 복권을 하지는 않지만 개발하면서 꽤 중요한 순간과 연관이 되어있습니다. 처음으로 코딩을 배우며 스스로 만들어 본 프로젝트가 복권 번호를 랜덤으로 발행하여 해당 등수를 나타내주는 것이였습니다. 그리고 객체지향을 배우며 왜 상속을 해야하는지.. 다형성이 왜 중요한지 도저히 이해할 수 없는 기간에 한국 복권과 미국의 복권(파워볼) 이 비슷해보이지만 번호의 범위, 등수 선정 등 세세한 규칙들이 다른 것을 보고 Lotto Class 를 KoLotto 와 EnLotto 클래스가 상속 받고 세세한 규칙이 다른 부분은 Lotto 클래스에서 abstract 로 해놓으면 되겠구나 등이 떠오르며 객체지향의 달콤함을 맛보았던 기억이 있습니다. 이런 경험 덕분인지 Selenium 을 가지고 어떤 것을 만들어볼까 했을 때 복권이 떠올랐습니다. 이 복권 구매 자동화를 가지고 좋은 정보들을 나눌 수 있을지 살펴볼 때 꽤나 만족스러운 환경이여서 선정하였습니다.  필자 프로젝트 환경 구성 언어 - python3.9 라이브러리 - selenium 브라우저 - chrome 이번에 만들어볼 프로젝트는 다양한 라이브러리가 필요로 하지 않으니 conda 등을 사용하지 않겠습니다.  1. 드라이버 다운로드 selenium 을 사용하기 위해서는 브라우저 드라이버가 필요합니다. 다운로드 홈페이지는 아래와 같습니다. 크롬 , 엣지 , 파이어폭스 , 사파리 저의 경우는 크롬을 기준으로 말씀드리겠습니다. 다른 브라우저를 사용하셔도 대부분의 selenium 사용 방법은 똑같지만 브라우저 option 설정 등이 좀 다르다고 볼 수 있습니다. 로컬 PC 의 브라우저 버전에 맞게 드라이버를 다운로드 받아야하기 때문에 버전을 먼저 확인 해야합니다. 드라이버 다운로드 를 버전에 맞게 해줍니다. 2. 드라이버 프로젝트에 임포트 저의 경우는 dirver 디렉토리에 다운로드 받은 드라이버를 넣었습니다. 3. selenium 설치 pip install selenium 4. 테스트 코드 작성 from selenium impo

[GIT] 간단한 코드 수정으로 expo 에 contributor 되기

이미지
expo 는 react-native 를 간편하게 개발할 수 있도록 도와주는 프레임워크이다. expo 의 관한 설명은 생략하고  expo 에 나의 PR 이 받아들여진 과정을 적어보려한다.  오픈소스에 참여하고 컨트리뷰터가 된다는 것은 개발자의 로망으로도 볼 수 있다. 개발을 배울 때부터 오픈소스에 타이포라도 고쳐서 기여하라고 많이 들었지만 사실 타이포를 발견하기 쉽지 않다.. 가끔 PR 목록들을 보면 어색한 문장이나 더 좋은 문장으로 변경하는 경우도 많은데 이는 웬만한 영어 실력이 아니라면 쉽지 않아 더 기회를 찾기는 어렵다. 필자의 경우는 expo 를 사용하여 개발을 진행하며 커스텀 폰트를 적용해야하는 상황이 생겼다. 이를 해결할 정보를 찾다 expo 공식 문서 에 커스텀 폰트 적용과 관련된 내용을 보게 되었다. 버그가 있는 것은 아니였지만 불필요하게 느껴지는 코드였다. 기존의 코드는 아래와 같았다. 위 코드는 간단히 의사코드로 표현하면 아래와 같다. if not 폰트로드:     return <AppLoading /> else:     return <SomeComponent /> 위 코드는 아래와 같이 개선이 가능하다. if not 폰트로드:     return <AppLoading /> return <SomeComponent /> 이러한 리펙토링은 더스틴 보즈웰, 트레버 파우커의  읽기 좋은 코드가 좋은 코드다 (The Art of Readable Code) 에서 함수 중간에서 반환하기 (Returning Early from a Function) 라고 불린다.  이와 비슷한 개념으로 마틴 파울러의 리팩토링 2판 에서는 중첩 조건문을 보호 구문으로 바꾸기 (Replace Nested Conditional with Guard Clauses) 이라고 불린다. 리펙토링 기법을 적용하여 PR 을 날렸다.  몇 문장 안되지만 시간은 거의 30분 40분이 걸린 것 같다. 영어를 잘 못하기 때문에 어색한 문장이기는 하겠지만 간단

[Linux] lsof 안될 때 포트 찾아 웹 서버 종료하기 Shell Script

  해당 코드가 필요한 상황 웹 서버 프로세스의 pid 를 찾아 종료해야할 때 잠깐?! 일하고 있는 곳에서 서버를 restart 해야하는 상황이 생겼다.(필자의 환경은 전자정부프레임워크 & tomcat 기반) 단순히 tomcat/bin 에 들어가 ./shutdown.sh 와 ./startup.sh 를 실행시키려 했지만 문제가 발생하였다. ./shutdown.sh 로 일부 프로세스는 종료되어있는데 일부 프로세스는 종료되지 않아 ./startup.sh 도 되지 않는 것이다. 꺼지지도 않고 켜지지도 않는... 그런 슬픈 상황이 발생하였다. 더 큰 문제는 포트를 찾으려고하는데 lsof 도 되지 않고 netstat 는 아주 일부 기능만 제공되는 것이였다. 해당 서버는 폐쇄망이라 새로운 것을 설치하기에는 불가능했다...  그렇다면 lsof 를 사용하지 않고도 웹 서버의 pid 를 찾아서 kill 을 하는 방법 밖에 없어 구글링을 한 결과 답변자 방향으로 절을 세 번 올릴만한 내용을 찾았다. 참고   필자의 경우는 일부 포트를 찾기 보다 열려있는 포트 중에서 java 관련있는 부분을 모두 kill -9 으로 종료해주었다. 스크립트 코드는 아래와 같다. pfiles /proc/* 2 > /dev/null | nawk -v port=$port ' /^[0-9]/ { cmd=$0; type="unknown"; continue } $1 == "SOCK_STREAM" { type="tcp" } $1 == "SOCK_DGRAM" { type="udp" } $2 == "AF_INET" { if((port!="")&&($5!=port)) continue; if(cmd!="") { printf("%s\n %s:%s/%s\n",cmd,$3,$5,type); cm

[JSP] JSTL contains 만들어 사용하기

  해당 코드가 필요한 상황 JSTL을 사용하여 리스트 안에 해당 원소가 들어있는지 비교가 필요할 때  JSTL 에서 제공하는 functions 안에 indexOf 함수가 있다. 이 indexOf 는 단순히 비교대상인 문자열이 있다면 시작 index 를 반환한다. 그리하여 이 indexOf 를 사용할 때는 fn:indexOf(A, B) > -1 과 비슷한 형태로 사용한다.   indexOf 를 주의해서 사용해야하는 것은 단순 문자열 비교라는 것이다.  아래 경우에서의 indexOf 는 문제없이 돌아갈 것이다. A : "1,2,3" B: "1" 하지만 이 경우를 생각해보자. 아래의 경우는 A의 원소 안에 B 의 원소가 들어있지 않지만 indexOf 를 사용하게 된다면 123 의 시작 index 를 반환하게 될 것이다. 그리하여 이러한 것보다 더 명확한 contains 를 구현하려한다. A: "1,15,123" B: "12" 필자의 상황은 A 의 원소가 B 의 원소 안에도 있는 것이라면 다른 액션을 취해줘야하는 상황이다. A : "1,2,3,4,5,6,7,8" B : "1,4,8" 일단 이 문제를 해결하기 위해서는 A, B 문자열을 ',' 를 구분하여 List 로 변환해야한다. 이 코드는 아래와 같다. <c:forEach var="item" items="${fn:split(A, ',')}"> 위의 forEach 문 안에서 B 의 원소가 있는지를 봐야한다. 안타깝게도 jstl 의 functions 에서는 List contains 를 제공하지 않는다. 그리하여 아래 코드와 같이 직접 contains 를 구현해야한다.  참고 <c:forEach var="A_ELEMENT" items="${fn:split(A, ',')}" varS

[Oracle] sequence 현재 값 수정하기

해당 코드가 필요한 상황 임의로 Sequence 의 값을 변경해야할 때 Oracle 12c 부터는 IDENTITY 를 사용하면 Auto Increment 의 기능을 제공합니다. 그렇지만 Oracle 12c 이전 버전에는 Sequence 나 Trigger 를 사용해서 키를 관리하는 것이 보편적인 방법입니다. 제가 잠깐?! 일하는 곳에서는 Primary Key 관리를 Sequence 를 이용하여 사용하고 있습니다.  이번 글에서는 Sequence 의 현재 값을 수정하는 것을 나누어보려합니다. 이 또한 흔히 찾아볼 수는 있지만 코드로 간략하게 있으면 좋겠다 싶어 글로 남깁니다. 이러한 상황이 필요한 저의 경우는 운영 서버의 데이터를 개발 서버로 migration 할 때 Sequence 값이 달라 문제가 발생하여 임의로 값을 증가시켜줘야했습니다. 아래 질의문을 간략히 설명하면  첫번째 질의문은 해당 Sequence 의 다음 증가 값을  1000000 을 증가하겠다고 설정한 것입니다. 두번재 질의문은 해당 Sequence 의 값을 증가 시킨 것 입니다.(설정한 1000000 이 증가됩니다) 세번째 질의문은 다시 해당 Seqeunce 의 다음 값을 1을 증가하겠다고 설정한 것입니다. ALTER SEQUENCE TABLE_SEQUENCE INCREMENT BY 1000000; SELECT TABLE_SEQUENCE.NEXTVAL FROM DUAL; ALTER SEQUENCE TABLE_SEQUENCE INCREMENT BY 1;

[JAVA] Gson.toJson 시 null 인 property 도 포함시켜 반환하도록 하기

Gson.toJson() 시 null 인 프로퍼티는 포함하지 않고 값을 반환한다. null 을 포함한 결과 값을 받고 싶으면 아래와 같이 GsonBuilder.serializeNulls 의 설정값을 주게 되면 사용할 수 있다. Gson gson = new GsonBuilder() .setPrettyPrinting() .serializeNulls() .create() ; return gson.toJson(OBJECT) ;

[Spring] Spring & Egov *.do 확장자 없이 URL 접근 가능토록 하기

 기존 프로젝트에서 *.do 확장자만 사용하다 API 기능을 추가해야하는 상황이 왔다. API 주소에 *.do 확장자를 사용하게 하고 사용하라고 하면 되지만.. 적절하지도 않고 크롤링도 아니고 API 인데 확장자를 *.do 를 쓰게 할 수는 없었다. 결과적으로 변경한 코드는 매우 간단했지만 꽤나 시간을 소비하였다. webapp/WEB-INF/web.xml 파일이 존재할 것이다. 이 부분에 servlet-mapping 설정 관련이 있을 것이다. 기존의 파일의 경우 아래와 같이 *.do 일 때 설정해놓은 servlet-class 로 이동토록 하였다.. <servlet-mapping> <servlet-name> action </servlet-name> <url-pattern> *.do </url-pattern> </servlet-mapping> url-pattern 을 *.do -> /* 이렇게 변경하면 서버 구동은 되겠지만 jsp 화면이 나오지 않을 것이다. 이유는 기본적으로 *.jsp, *.js 등 servlet-mapping 이 따로 설정되어있었기 때문이다. 그렇기에 /* 로 변경하면 *.jsp 도 위 action 에 설정이 될 것이다. 그렇다면 어떻게 해야하는가? /* -> / 로 변경하면 된다. <servlet-mapping> <servlet-name> action </servlet-name> <url-pattern> / </url-pattern> </servlet-mapping> 위와 같이 하면 화면은 보이겠지만 img 등은 보이지 않을 것이다. 이유는 url-pattern 을 /  으로 설정하여 기존에 있던 기본 servlet 을 우리가 덮어 사용하였기 때문이다. 여러 파일을 제대로 불러와주기 위해서는 아래와 같은 추가적인 servlet-mapping 이 필요하다. ( 출처 ) <servl

[React Native] absolute 속성으로 가로 채우기(Full Width)

이미지
RN 에서 flex 를 사용하게되면 자동으로 최대 사이즈를 가지게 된다. absolute 를 사용하게 됐을 때 가로 채우는 방법을 포스팅하려한다. 매우 간단하지만 초반엔 width 를 가지고 해보았지만 자식 노드 스타일이 예상치 못하게 적용되어 다른 방법을 찾아보았다. 찾은 방법이 훨씬 간단하고 정답에 가까워 보인다. 아래 코드에서 top: 0 을 지우게 되어도 같은 결과가 나오게 된다. 만약 위 message 알림이 하단에 나오길 바란다면 top: 0 을 지우고 bottom: 0 을 주게 되면 된다. top: 0 과 bottom: 0 을 같이 사용하게 된다면 전체 화면을 사용할 수 있다. <View style={{ position: 'absolute', top: 0, left: 0, right: 0, alignItems: 'center', backgroundColor: conceptColor.sky, }} > <View style={{ height: 48 }} /> <Text>text</Text> </View> 위 코드의 실행 화면은 아래 사진과 같이 나온다.  정리 top:0 => 상단 위치 , 너비(width) 자식 노드의 스타일에 따라 유동적 left:0 => 왼쪽 위치 , 너비(width) 자식 노드의 스타일에 따라 유동적 right:0 => 오른쪽 위치 , 너비(width) 자식 노드의 스타일에 따라 유동적 Bottom:0 => 하단 위치 , 너비(width) 자식 노드의 스타일에 따라 유동적 top:0, left:0, right:0 => 상단 위치, Full Width bottom:0, left:0, right:0 => 하단 위치, Full Width top:0, bottom:0, left:0, right:0 => Full Scre

[React Native] xcode에서 ios 실행시키기 + ld: Library not found for -LDoubleConversion 에러 해결

이미지
React Native를 개발하면서 xcode로 실행을 시도하였습니다.  Android Studio에서는 실행을 해보았습니다. 단순히 React Native(이하 RN) 프로젝트 안에 ios 폴더를 열면 되지 않을까 했습니다. 다행스럽게도 ios 폴더 안에 .xcodeproj 파일이 있습니다. 이 파일은 확장자 의미대로 프로젝트 관리 파일입니다. .xcodeproj 파일을 여시면 xcode가 열리고 단순히 빌드가 진행이 됩니다. 저의 경우는 에러가 나옵니다. 아래 사진과 같이   ld: Library not found for -lDoubleConversion 에러가 나옵니다. 여러 방법을 찾아보았지만 해결되지 않다가 해결한 방법을 공유하려합니다. 당연히 . xcodeproj 를 열었지만 제가 해결한 방법은 . xcworkspace 파일을 연것입니다. 이 파일을 열게되면 마찬가지로 xcode가 열리지만 왼쪽에 폴더 구조를 보면 약간은 다릅니다. 맨 처음 열게 되면 빨간 네모 내용이 없고 No Schema 가 나와있을 것 입니다. 이 상태에서 실행을 한다면 빌드는 되더라도 시뮬레이터가 켜지지는 않습니다. No Schema 상태에서는 No Schema를 클릭 하시고 스키마를 생성해주세요. 시뮬레이터가 자동으로 지정이 되고 실행 버튼을 누르시면 앱이 작동하는 것을 확인하실 수 있습니다.

[서비스 만들기] #8 - 업체에 위탁하여 상표 등록하기.

창업을 준비하다보니 상표가 필요해졌습니다. 모바일 서비스를 개발하여 배포하려고 하니 더욱 로고가 필요합니다. 수많은 갈등 속에서 로고를 만들었습니다. 이 로고는 저작권이 없는 상태이기에 누구든지 먼저 선점할 수 있는 상태입니다. 상표 등록을 하지않는다면 상표권 침해로 추후 문제가 생길 수 있습니다. 만든 로고(상표)를 사용하려면 특허청에 등록해야합니다. 심사를 통해 최종적으로 출원이 됩니다. 개인이 직접 특허청에 상표 등록 신청을 할 수 있지만 저는 업체를 통해 신청을 하였습니다. 1. 상표 등록 신청 과정 최소화 직접 등록을 하게 된다면 많은 시간이 소요되며 실수가 발생하여 번거로울 수 있다. 2. 출원 전 조사가 가능 마크인포의 경우 조사 출원 서비스 를 이용하게된다면 변리사가 상표등록가능성을 조사하여 알려주게됩니다. 3. 생각보다 저렴한 수수료 회사를 거치게 되니 수수료가 있지만 10만원 정도이다. 수수료는 있지만 회사를 통해 등록하게 되니 시간이 줄어들어 크게 보면 비용도 절감된다고 판단이 되어 업체에 맡기었습니다.

[서비스 만들기] #7 - GCP Github 연동하기.

이미지
GCP(Google Cloud Platform) 에는 Cloud Source Repositories  라는 서비스가 있습니다.  서비스 만들기 시리즈 에서도 Cloud Source Repositories를 다룬 글이 있습니다.  초반에는 Github 사이트를 이용하지 않고 Cloud Source Repositories 내에 있는 private git repositories를 사용했습니다.  그러나 지금은 Github 사이트와 연동이 되어있습니다. 이유는 간단한데요. Github에 존재하는 기능 사용에 제한이 있다는 것 입니다. 제가 가장 필요했던 기능은 PR, Branch protecion rules 즉 특정 브랜치에는 push가 되지않는 것입니다. PR을 거쳐야 병합이 가능해집니다. Pull Request Branch protection rules 이 기능은 GCP Cloud Source Repositories에서 제공해주지 않습니다.  그래서 Github에 레포지토리 생성하고 GCP에 연결하였습니다. Devops 쪽은 Azure가 제일 좋은 것 같습니다.  그렇다면 Cloud Source Repositories는 쓸모가 없는 것인가. 그렇지 않습니다. 다양한 장점을 가지고있습니다. CI / CD 로 빠르게 피드백을 처리할 수 있습니다. 쉽게 트리거를 설정하고 테스트를 할 수 있습니다. 코드 찾기도 굉장히 빨라지고 많은 정보를 줍니다. 그러므로 개인적인 생각으로는 Cloud Source Repositories에 Github를 연동하는 것이 가장 효과적이지 않나 싶습니다. Source Repositories 이제 Cloud Source Repositories에 Github 연동해보겠습니다. GCP Cloud Source Repositories 에 들어가줍니다. 프로젝트 클릭해줍니다. Connected repositories 클릭 해줍니다. Connect a repository 클릭

[서비스 만들기] #6 - GCP App Engine에 도메인 연결하기(Custom Domain)

이미지
당연히 서비스를 만들려면 홈페이지 하나쯤은 있어야겠죠? 서버는 만들었지만 192.*~ 이런 주소로 접근해야한다면 말이 안되는 서비스일 것입니다. DNS를 사용하고싶다면 도메인을 가지고있어야합니다. 도메인을 구매하려면 Goolge Domain 에서 구매해주세요. 가격은 최상위 도메인마다 다르지만 저의 경우 .com으로 구매하여 연간 12달러 지불하도록 하였습니다. 도메인을 소유하셨다면 구글 앱 엔진(GAE)와 연결해보겠습니다. 도메인 맵핑  구글 문서입니다. 위 문서를 얼핏보고 따라하시면 아마 맵핑이 안되실겁니다.(이건 제 경험...) 1. 구글 클라우드 콘솔 -> 앱 엔진 -> 설정 -> 커스텀 도메인 구글에서 가져온 사진 1번에서 도메인을 선택하시고 verify 눌러주세요. 구글에서 가져온 사진 2단계는 서브 도메인을 적어주는 곳입니다. 기본적으로 www가 추가되어있습니다. 서브 도메인 있으시면 추가해주시고 3번으로 넘어가주세요. 자 이렇게 보면 간단하게 설정이 끝난 것으로 보이지만 앱 엔진의 설정을 보시면 ssl보안 칸에 노란색 경고 표시보이시나요. 저 경고 표시가 나타나며 맵핑한 주소로 들어가시면 사이트가 맵핑이 안되어있을 겁니다 제가 맨위에 링크 걸어드린 구글 문서를 따라한 결과입니다. 무지한 저는 5일간 헤매다 맵핑을 성공했습니다. 추가적인 작업을 포스팅하겠습니다. 2. Network serivces에 Cloud DNS에 들어가주세요. 3. Zone name과 Dns name을 적어주세요. 영역 이름은 일반적으로 DOMAIN-dot-com 이런 형식으로 적는 것 같습니다. 저의 경우는 rally-dot-com 이렇게 했습니다. DNS name 영역은 서브 도메인 말고 구매한 메인을 적어주세요. DNSSEC 는 Domain Name System Security Extension의 약자입니다. dns는 보안 공격에 취약합니다. d

[서비스 만들기] #5 - GCP App Engine에 빌드 트리거 이용해서 Node js 배포하기

이미지
Git 커밋이 될 때 마다 자동 빌드되도록 하는데 구글 클라우드 떠날뻔 했습니다. 어렵지는 않지만 CI 구축을 처음하다보니 많이 헤맨거같습니다. 포스팅하려고 자료준비하다보니 간단하네요. 제가 이전에 포스팅한 App Engine에 Node js 배포하기.  에서는 터미널에서 명령어를 쳐서 배포하는 것을 해보았습니다. 배포가 되셔야 이번에 진행이 되십니다. 이번에는 빌드 트리거와 연동하여서 자동 배포를 구축해보도록 하겠습니다. 도커를 이용해서 빌드하실 분은  Dockfile 이용해서 빌드 트리거 사용하기  참고해주시면 됩니다. 구글 클라우드 떠날뻔한 흔적... 1. 빌드 트리거 설정 빌드 트리거 생성하실 때 Dockfile로 하시면 안되고 밑에 Cloud Build 구성 파일 선택해주세요.  2. 프로젝트 폴더에 cloudbuild.yaml 파일 생성. steps: - name: 'gcr.io/cloud-builders/gcloud' args: ["app", "deploy"] dir: 'server' timeout: '600s' 3. 커밋해줍니다. 빌드 기록 에 들어가셔서 기록을 확인해주세요. 그럼.... 되실겁니다. 혹시나 아래와 같이 로그가 남고 실패할 경우 cloudbuild.yaml 경로를 변경해주세요. 저의 경우 build 폴더를 만들고 cloudbuild.yaml를 넣어줬습니다. 찾아보니 뭐 이미 파일 저장소에 있던 cloudbuild.yaml 하고 충돌이 난거라고합니다. ERROR: (gcloud.app.deploy) There is a cloudbuild.yaml in the current directory, and the runtime field in /workspace/server/app.yaml is currently set to [runtime: nodejs]. To use

[서비스 만들기] #4 - GCP App Engine에 Node js 배포하기

Node js App Engine 배포하기.  이번에도 고생을 하여 포스팅을 합니다... 구글 문서는 좀 생략이 된 부분이 있네요!! 저같이 경험이 부족한 사람은 해메네요. 정리를 하니 간단합니다!! App Engine 표준 환경의 Node.js를 위한 빠른 시작 1. Node js 폴더 생성 2. 폴더 안에 app.yaml 추가 app.yaml 코드는  구글 제공 샘플 코드  참고하였습니다. runtime: nodejs env: flex manual_scaling: instances: 1 resources: cpu: 1 memory_gb: 0.5 disk_size_gb: 10 3.터미널에서 gcloud 명령어를 쳐줍니다. PROJECT_ID는 프로젝트명-12345678 이런 형식을 찾아서 적어주시면 됩니다. gcloud components update gcloud projects create [YOUR_PROJECT_ID] --set-as-default 4. 프로젝트 생성이 되었는지 확인합니다. gcloud projects describe [YOUR_PROJECT_ID] 그럼 아래와 같이 정보가 표시됩니다. createTime: year-month-hour lifecycleState: ACTIVE name: project-name parent: id: '433637338589' type: organization projectId: project-name-id projectNumber: 499227785679 5. App Engine 초기화하고 리전을 선택합니다. gcloud app create --project=[YOUR_PROJECT_ID] 6. 결제 정보 등록 7. 배포 gcloud app deploy 8. 배포 확인 gcloud app browse 아직까지도 설정하는 부분이 어렵기는 합니다! 더 친숙해지도록 부딪히고 또 포스팅하겠습니다!

[서비스 만들기] #3-1 - 구글 트리거 만들기. Dockerfile 사용해서 자동 빌드 설정하기

이미지
Node js CI 설정하는데 애먹어서 포스팅을 해야할 것 같습니다... Master 브랜치에 올라온 코드가 빌드 실패가 되면 큰일인데요. 그래서 커밋이 될 때 마다 빌드 검사를 하도록 하는 제도가 필요합니다. 이번 포스팅에서는 빌드 검사하는 기능을 구축해보겠습니다. CI 서버를 말이죠. 구글 가이드 라인 홈페이지 입니다. 이곳에 들어가셔서 시작하기 전에 목록에 나와있는대로 진행은 해주셔야합니다. 구글은 Cloud Build를 사용하여 빌드를 하게 되는데요. Cloud에서 빌드를 하기 위해서는 단순히 소스 파일만 올려서는 자동으로 해주지 않기 때문에 설정이 필요합니다. 구글은 Docker 이미지를 빌드합니다. 그리고 Container Registry에 이미지를 올리게 됩니다. 뒤에 보겠지만 Cloud Build에서 빌드된 파일은 이미지와 소스 파일을 다운로드 받을 수 있습니다. 1. 소스 저장소에 들어가 환결설정 아이콘을 클릭해줍니다.  소스 저장소는  이전 포스팅 참고. 2. Cloud Build 트리거 클릭해줍니다. 3. 저장소 연결을 클릭하여 연결해줍니다. 저는 이것만 하면 되는줄 알고 소스 파일 커밋했다가 실패났습니다. 추가로 설정할 게 많이 남았습니다.. 4. 트리거 설정 화면이 나옵니다. 각자의 상황에 맞게 설정해줍니다. 제가 포스팅하는 것은 빌드 구성에서 Dockerfile 입니다. 이것만하고 트리거 실행하면... 무조건 에러납니다. 그래서 살짝 멘탈이 흔들렸지만 찾았습니다. 5. Node js 도커라이징 Dockerfile 생성에 대하여 상세한 설명이 나와있습니다. 미리 소스 보기를 하자면 아래와 같습니다. 마지막 줄  CMD[~~, " app.js "] app.js는 시작할 때 불러지는 파일을 말합니다. 에를 들어 저의 경우 node app.js하면 서버는 작동합니다. 마지막 줄의