이유없이.... npm run dev 가 먹히질 않는다....

구글링을 열심해 해 봐도 이런저런 상황에 대한 예상만 있을뿐 적당한 답은 없다....

도대체 이유가 뭘까.......

그러다가 Atop 에 설치한 command prompt 가 아닌 window 의 cmd 창을 열어서 해봤는데....

잘된다.....응???? 응???

내가 이것때문에 어젯밤에.....하....




spring boot + spring security + mybatis + vue 로 
로그인과 허접해 보이지 않는 화면을 만들(포팅)었으니 이제 실제 데이터를 가지고 와서 뿌려주는 작업을 해 보자. 

Vue.js - Ajax 에서 이미 데이터 가지고 오는 것은 해 보았으니 프로젝트에 반영만 하면 된다.

User.vue 에 사용자 목록을 불러와 그려보자. 
우선 Grid.vue 를 하나 만든다. 
데이터를 가져와 grid 를 만들고 sorting 과 filtering 기능을 제공하는 간단한 grid 이다. 
예전에 만들었던 10만건 이상 처리 가능한 grid 는 나중에 포팅해 보도록 하고.... 일단은 simple 한 data grid 를 만들어 본다. 

<template>

  <div id="yhkimgrid">

    <table>

    <thead>

      <tr>

        <th v-for="key in columns" @click="sortBy(key)" :class="{ active: sortKey == key }">

          {{ key | capitalize }}

          <span class="arrow" :class="sortOrders[key] > 0 ? 'asc' : 'dsc'">

          </span>

        </th>

      </tr>

    </thead>

    <tbody>

      <tr v-for="entry in filteredData">

        <td v-for="key in columns">

          {{entry[key]}}

        </td>

      </tr>

    </tbody>

  </table>

  </div>

</template>


<script>

export default {

  name: 'Grid',

  props: {

    data: Array,

    columns: Array,

    filterKey: String

  },

  data () {

    const sortOrders = {}

    this.columns.forEach(key => {

      sortOrders[key] = 1

    })

    return {

      sortKey:'',

      sortOrders: sortOrders

    }

  },

  computed: {

    filteredData() {

      const sortKey = this.sortKey

      const filterKey = this.filterKey && this.filterKey.toLowerCase()

      const order = this.sortOrders[sortKey] || 1

      let data = this.data

      if (filterKey) {

        data = data.filter( row => {

          return Object.keys(row).some( key => {

            return String(row[key]).toLowerCase().indexOf(filterKey) > -1

          })

        })

      }

      if (sortKey) {

        data = data.slice().sort( (a, b) => {

          a = a[sortKey]

          b = b[sortKey]

          return (a === b ? 0 : a > b ? 1 : -1) * order

        })

      }

      return data

    }

  },

  filters: {

    capitalize: function (str) {

      return str.charAt(0).toUpperCase() + str.slice(1)

    }

  },

  methods:{

    sortBy: function (key) {

      this.sortKey = key

      this.sortOrders[key] = this.sortOrders[key] * -1

    }

  }

}

</script>


<!-- Add "scoped" attribute to limit CSS to this component only -->

<style scoped="">

#yhkimgrid {padding-top:10px}

table {

  border: 2px solid #42b983;

  border-radius: 3px;

  background-color: #fff;

}


th {

  background-color: #42b983;

  color: rgba(255,255,255,0.66);

  cursor: pointer;

  -webkit-user-select: none;

  -moz-user-select: none;

  -ms-user-select: none;

  user-select: none;

}


td {

  background-color: #f9f9f9;

}


th, td {

  min-width: 120px;

  padding: 10px 20px;

}


th.active {

  color: #fff;

}


th.active .arrow {

  opacity: 1;

}


.arrow {

  display: inline-block;

  vertical-align: middle;

  width: 0;

  height: 0;

  margin-left: 5px;

  opacity: 0.66;

}


.arrow.asc {

  border-left: 4px solid transparent;

  border-right: 4px solid transparent;

  border-bottom: 4px solid #fff;

}


.arrow.dsc {

  border-left: 4px solid transparent;

  border-right: 4px solid transparent;

  border-top: 4px solid #fff;

}

</style>


User.vue 에서는 위에서 만든 grid component 를 이용해 목록을 뿌리고 filter 할 수 있도록 input 하나 만들고 ajax 로 데이터를 불러오면 된다. 

<div id="User">

    <form id="search">

      Search <input id="queryinput" name="query" v-model="searchQuery">

    </form>

    <yhgrid :data="gridData" :columns="gridColumns" :filter-key="searchQuery"></yhgrid>

  </div>

</template>


<script>

import axios from 'axios'

import yhgrid from '@/components/Grid'

export default {

  name: 'User',

  components: {

    pnpgrid

  },

  data () {

    return {

      searchQuery: '',

      gridColumns: ['idx', 'name', 'id', 'email', 'cellphone'],

      gridData:[]

    }

  },

  created(){

    this.load()

  },

  methods:{

    load(){

      axios.get('http://testip/firstproject/api/userlist')

      .then(res => {

        this.gridData = res.data;

      }).catch(e => {

        console.error(e)

      })

    }

  }

}

</script>


<!-- Add "scoped" attribute to limit CSS to this component only -->

<style scoped="">

#queryinput {width:310px; margin-left: 10px}

</style>




Spring Boot 에서는 예전에 만들어 놓은 APIController 에서 member list 를 불러와 return 하면 끝.
@RestController
public class APIController {
	
	@Autowired
	MemberService service;

	@RequestMapping(value="/api/userlist", method=RequestMethod.GET)
	public List getUserList() {
		return service.getMemberList();
	}
}

결과는 굳!








eclipse / sts 로 개발 후 코드리뷰 시 Team Synchronizing 으로 이동한 후 리뷰할 파일을 open 하면 

상단에 Java Structure Compare 가 나타나고 그 밑에 실제 소스코드 비교인 Java Source Compare 가 나타난다.

보통 Java Source Compare 부분을 최대화 한 후 리뷰하는데 상단의 Java Structure Compare 부분이 거슬리는 경우가 있다. 

window > preferences > General > Compare/Patch > General tab > Open structure compare automatically 를 체크 해제 하면 된다.




'IT > Eclipse' 카테고리의 다른 글

structure compare hide  (0) 2018.09.20
eclipse 가볍게 사용하기  (0) 2014.01.23
eclipse bookmark  (0) 2014.01.23
selected svn connector library is not available or cannot be loaded  (0) 2014.01.06

Controller 에서 parameter를 받는 방법은 다양하다. 

httpServletRequset.getParameter(), 아주 일반적인 방법이다.
@RequestMapping("/test")
public String test(HttpServletRequest req) {
    String userId = req.getParameter("userId");
    return "test";
}


httpServletRequest.getAttribute(), getParameter() 와 비슷하다. 

getParameter()는 String을 처리하며 getAttribute는 Object 를 처리한다.

<%@ page limport="com.yhkim.study.vo.UserDto" %>
<%
    UserDto user = (UserDto)request.getAttribute("user");
    String userId = user.getUserId();
%>

@RequestParam
name, required, defaultValue 라는 속성을 가지고 있다. 
required 값이 true 일 경우 해당 parameter는 반드시 request에 담겨져 있어야 한다. (없을 경우 400 에러 발생)
@RequestMapping("/test")
public String test(@RequestParam(value="userId", defaultValue="yhkim") String userId) {
    return "test";
}

간단하게 아래와 같이 사용할 수도 있다. 

이렇게 할 경우 required는 false 가 되며 변수명과 동일한 파라미터만 받을수 있게 되며, defaultVal 은 설정할 수 없다.

@RequestMapping("/test")
public String test(String userId) {
    return "test";
}


@RequestBody GET 방식은 Request Packet에 Body 가 존재하지 않기 때문에 @RequestBody로 받으려면 반드시 POST 여야 한다. 
JSON이나 XML같은 데이터를 적절한 messageConverter로 읽을때 또는 DTO/VO 객체 전체로 받을경우 사용한다.
@RequestMapping("/test")
@Slf4j
public String test(@RequestBody UserDto user) {
    log.info(user.getUserId());
    return "test";
}


@ModelAttribute @RequestParam과 비슷한데 1:1로 parameter를 받을경우 @RequestParam을 사용하고, DTO/VO로 받을경우 
@ModelAttribute로 받을 수 있다. validation작업을 추가로 할수 있다. 
받아오고자 하는 데이터의 이름을 지정하여 해당 데이터만 가져오고자 할 때 사용할 수 있다.
@RequestMapping("/test")
@Slf4j
public String test(@ModelAttribute UserDto user, @RequestParam(value="anotherParam", defaultValue="") String anotherParam) {
    log.info(user.getUserId());
    log.info("another paraemter is {}", anotherParam);
    return "test";
}

상황에 따라 적당한 방법으로 parameter 를 받아 처리하면 된다. 
Ajax로 객체배열을 넘기고자 할 때 @ModelAttribute 로 받을 수도 있고(객체 안에 List로 Type의 변수로 가지고 있을 경우) 
@RequestBody로 받을 수도 있다. (JSON.stringify()를 이용해 배열형태로 받기)
어떻게 받아서 처리하고 싶은지 잘 선택해서 하자. 




예외처리는 try ~ except 문을 통해 처리 한다. 기본 문법은 아래와 같다.

try: 
   # do something
except EOFError:
  print('why did you do an eof on me?')
else:
  print('No exception')

다른 언어에서 처럼 예외클래스를 custom 하게 만들 수도 있다. 
마찬가지로 finally 를 이용하면 예외가 발생하더라도 안전하게 후처리를 할 수 있다.
class ShortInputException(Exception):
    '''A user-defined exception class'''
    def __init__(self, length, atleast):
        Exception.__init__(self)
        self.length = length
        self.atleast = atleast

try:
    f = open('abc.txt')
    text = input('Enter something => ')
    if len(text) < 3:
        raise ShortInputException(len(text), 3)
except EOFError:
    print('why did you do an eof on me?')
except ShortInputException as ex:
    print('ShortINputException: The input was {} long, expected at least {}'.format(ex.length, ex.atleast))
else:
    print('No exception was rasied')
finally:
    if f:
        f.close()
위 예제 코드에서 본 것 처럼 except 절에 as 를 이용하면 짧은 이름으로 사용가능 하다. 

 java에서도 try-catch-finally 를 사용하지 않고 try-with 문을 이용하는 것 처럼 
python 에서도 동일하게 with 문을 이용할 수 있다.
with open('abc.txt') as f:
    for line in f:
        print(line)



'IT > Python' 카테고리의 다른 글

Python - 예외 처리  (0) 2018.09.18
Python - Socket(Client) with struct (쉽게 테스트용 Client 만들기)  (0) 2018.09.11
Python - I/O, File I/O, pickle  (0) 2018.09.10
Python - Class  (0) 2018.09.07
Python - 자료구조  (0) 2018.08.30
Python - 함수  (0) 2018.08.27

지난번 #아디다스 #이지부스트 700 #온라인래플 은 
언제나 그랬듯이 역시나 꽝! 다음기회에...였다... 뭐 #나이키 #드로우 와 마찬가지로 늘 꽝이였기 때문에 상실감은 없다.

그런데 오늘 아디다스에서 한번 더 희망고문을 한다.
지난번 이지부스트 온라인 래플 1회 이상 응모고객 중 미당첨 고객에 한해 우선 구매의 기회를 부여하는 것! (선착순 결제 방식)


와우....

근데 금요일 밤 새벽 1시 부터 한다고???.... 음.....일찍자고 일찍 일어나서 출근해야 하는데... 이거 해야 하나 말아야 하나...

#yeezyboos 350은 사실 700 보다 개인적으로는 더 예쁘다고 생각하는데 이번에 발매하는 TRIPLE WHITE 는 말그대로 너무 엄청 화이트라... 좀 ... 부담스럽다...(음...그래도 하나 겟 하고 싶다.)



이번엔 한번... 기대해볼까....????

링크 타고 들어가 보니 대기자 2만명 있던데....ㅋ.....;;

아주 오랜기간동안 알레르기성 비염과 함께 살고 있다.

수술을 권장하는 병원도 봤다. (돈벌이에 눈이 먼 병원이라고 보인다.)

알레르기 반응을 없애지 못한다면 수술은 아주 일시적인 효과만 볼 수 있을 것이다. 


알레르기란?

외부물질에 대해 자기 신체 방어기전, 즉 면역이 과도하게 반응하거나 변형된 반응으로 인하여, 두드러기, 비염, 천식 등의 이상 과민반응을 일으키는 것을 말합니다.

알레르기 증상
※ 알레르기성 비염
알레르기성 비염이란 코 점막이 특정물질에 대하여 과민반응을 나타내는 것으로 연속적인 재채기 발작, 계속 흘러내리는 맑은 콧물(수양성 비루), 코막힘(비폐색) 등이 특징적인 증상인 알레르기성 질환입니다. 계절성은 꽃가루에 의하여 생기므로 화분증이라고도 하며 통년성은 집안의 먼지, 진드기, 진균이 주요한 항원이 됩니다.

※ 기관지 천식
천식이란 숨 쉴 때 들어오는 여러 가지 자극 물질에 대한 기관지의 과민반응으로, 기관지를 비롯한 기도 점막에 염증이 생겨 부어오르며 기관지가 좁아져서 천명(쌕쌕거리는 호흡음)을 동반한 기침과 호흡곤란이 발작적으로 나타나는 질환입니다. 일단 호전이 되면 대부분의 경우 거의 정상 상태로 회복이 되기는 하나 반복적으로 자주 재발하는 특징을 가진 호흡기질환입니다.

※ 알레르기성 피부염
알레르기 면역 반응에 의해 나타나는 피부증상을 분류한 것으로 알레르기성 피부질환에는 아토피성 피부염, 접촉성 피부염, 두드러기, 곤충 알레르기, 식품 알레르기, 약품 알레르기 등이 포함됩니다.
알레르기의 예방과 치료
※ 꽃가루
① 바람이 강하고 맑은 날은 공기 중에 꽃가루의 양이 많으니 외출을 피합니다.
② 꽃가루 유행철의 외출 시에는 마스크와 안경을 착용합니다.
③ 외출 후 집에 들어가기 전에는 옷에 붙은 꽃가루를 털고 귀가 후에는 반드시 손과 얼굴을 씻고 가글을 합니다.
④ 실내 공기 청정기를 사용합니다.

※ 집먼지, 집먼지진드기
① 실내 통풍을 원활히 시키고 청소를 자주 합니다.
② 진드기가 잘 번식할 수 있는 카펫트는 되도록 사용하지 않습니다.
③ 메트리스 속감의 먼지는 집먼지 진드기의 근원이 되므로 침구를 깨끗이 청소하고 햇빛에 자주 건조시킵니다.
④ 진드기가 통과하지 못하는 고밀도 섬유를 사용합니다.
⑤ 에어컨 필터를 자주 청소합니다.
⑥ 집먼지진드기의 상피껍질은 9~10월에 최고농도를 이루므로 이 시기를 주의합니다.

※ 곰팡이(진균)
① 곰팡이 포자는 5~7월, 9~11월에 많이 발산하므로 이 시기를 주의합니다.
② 곰팡이가 서식하기 좋은 환경인 욕실, 부엌, 창고에 환풍을 자주 합니다.
③ 세탁물을 실내에서 건조시키지 않습니다.
④ 벽 등의 곰팡이는 곰팡이 제거제를 사용해서 제거합니다.

※ 동물
① 알레르기의 원인이 되는 동물을 가까이 하지 않도록 합니다.
② 동물의 털에 의해 알레르기가 발생하는 경우가 많으므로 애완동물을 자주 목욕시키도록 합니다.

※ 음식
① 원인이 되는 물질을 포함한 식품은 먹지 않도록 합니다.
② 음식을 피하는 경우, 필요 이상으로 제한하여 건전한 발육과 성장에 영양분이 부족하지 않도록 반드시
주치의의 지시를 받도록 합니다.


우리는 우리몸이 어떤 환경과 음식에 반응 하는지 알아야 한다.  그리고 그 환경과 음식을 멀리하는 것이 최선이다. 

나는 D. farinae (d2) / 진드기(Df) 수치가 가장 높으며 (4.62)

D. pteronyssinus (d1) / 진드기(Dp) 가 두번째로 높다. (2.67)

그 외 집먼지 House dush(h1) / 집먼지 수치가 0.94, 개털 (0.31), 토마토 (0.30) 등이 있다.

결국 집먼지 진드기와의 싸움이다...


아침부터 만화보여달라고 울고불고 난리치는 크레이지 모드 다섯살 첫째딸과 한바탕 싸운 후 
화해하고 밖으로 나왔다.
어젯밤 비가 와서 인지 숲세권 아파트의 아침 풍경은 싱그러웠고 푸르렀다. 
가볍게 산책을 하며 예쁜꽃, 빗방울이 또르르 떨어지는 풀잎을 보니 기분이 좋아진다.


#육아 #아빠와딸 #아빠육아 #꽃선물 #예쁜꽃 #숲세권
이제 곧 한가위.!!

회사에서 추석선물로 준비한 랜덤 책 선물 보자기!!

보자기 속에는 3권의 책이 들어 있다.

#어른의것 #누구의인정도아닌 #이동진독서법

#독서 는 우리를 풍요롭게 한다.

지금 읽고 있는 #사피엔스 라는 #책 을 다 읽고 긴 연휴 동안 책과 함께 마음도 풍성한 한가위를 맞이해 보련다.

책은 참 좋은 #선물 이다.!!

Ehcache 에 대해서는 이전글 참고 :

2013/12/24 - [IT/Spring] - ehcache

diskExpiryThreadIntervalSeconds: 디스크에 저장된 캐시들에 대해  만료된 항목를 제거하기 위한 쓰레드를 실행 할 주기 설정

diskSpoolBufferSizeMB: 디스크 캐시에 쓰기 모드로 들어갈때, 사용될 비동기 모드의 스폴 버퍼 크기 설정, OutOfMemory 에러가 발생 시 수치를 낮추도록 한다.
diskPersistent: VM이 재기동 할때 캐싱된 객체들을 디스크에 계속 유지 할지 여부
diskAccessStripes: 디스크 퍼포먼스를 조정하기 위한 스트라핑 설정
eternal: 시간설정에 대한 무시 설정 (boolean), true 면 모든 timeout 설정은 모두 무시 되고 Element에서 캐시가 삭제 되지 않음
maxElementsInMemory: 메모리에 캐싱 되어질 객체의 최대수
maxEntriesLocalHeap: 힙메모리 최대량
memoryStoreEvictionPolicy: 객체의 갯수가 설정된 maxElementsInMemory에 도달 했을 경우 메모리에서 객체들을 어떤게 제거 할지 에 대한 제거 알고리즘
     FIFO First In First Out. 
            Cache에 node를 추가할 경우 등록시간을 기록하여, 지정한 Cache size가 overflow가 발생할 경우에, 
            등록시간이 가장 빠른 node를 입력된 node로 변경하여 Cache에 관리한다.
     LFU Least Frequently Used. 
            가장 호출이 안된 node를 overflow 발생시 삭제하고, 새로 등록된 node를 등록하는 알고리즘으로, 
            Cache에 등록된 값을 호출할 때 count값과 위치를 함께 관리하여, overflow 발생할 때 
            count값이 제일적은 node를 삭제한다.
     LRU Least Recently Used. 
            가장 오랫동안 사용이 안된 node를 overflow발생시에 삭제하고, 
            새로 등록된 node를 등록하는 알고리즘으로, Cache에 등록된 값을 호출할 때 
            호출된 node 의 위치를 제일 앞으로 이동한다. overflow가 발생할 경우, 마지막 node를 삭제하여 관리한다.
clearOnFlush: flush() 메소드가 실행 될 때 메모리 캐시가 바로 살제 할지 여부, 기본적으로 true 이며 바로 삭제됨.
name: 캐시 이름
overflowToDisk: maxElementsInMemory 음계량에 가까우면 오버플로우되는 객체들을 디스크에 저장 할지 결정
timeToIdleSeconds: 다음 시간 동안 유휴상태(Idle) 후 갱신 할 지 설정(default: 0)
timeToLiveSeconds: 다음 갱신 하기 까지 캐쉬가 유지 되는 시간 (0이면 만료시간을 지정하지 않는다고 보고 유지 되지 않음, default: 0)
maxElementsOnDisk: 디스크 캐시에 저장 될 최대 객체의 수를 지정
maxEntriesLocalDisk: 로컬 디스크에 유지 될 최대 객체 수
maxEntriesInCache: Terracotta의 분산 캐시에서만 사용 가능하며, 클러스터링에 저장 할 수 있는 최대 캐시 수를 설정
transactionalMode: copyOnRead , copyOnWrite 시 트렉젝션 모드를 설정
statistics: JMX 통계정보 갱신 옵션
copyOnRead: 읽기때 캐시 요소를 복사 할 지 여부
copyOnWrite: 캐시 객체 쓸 경우 위한 복사 할지 여부
   - copyOnRead와 Write는 캐쉬로 받아온 객체에 수정이 일어나는 경우 사용한다.
   - 캐시된 객체에 수정이 일어나면 참조호출로 인해 그 뒤에 호출되는 모든 객체가 수정 영향이 중첩되어 발생하므로 주의 필요"
logging: 로깅 사용 여부
overflowToOffHeap: Off-heap 메모리 사용을 설정을 사용 할 수 있으며 JAVA의 GC에 영향을 주지 않는다. 엔터프라이즈 버전에서만 사용가능 하며,   maxEntriesLocalHeap 설정을 최소한 100 요소까지 권정하며 OffHeap를 사용하는 경우 성능이 저하될수 있음.
maxMemoryOffHeap: Off-heap 메모리 사용의 최대 량을 설정
maxBytesLocalHeap: 최대 로컬 힙메모리 사용량 설정, 1k, 1m, 1g 해당 옵션을 사용할 경우 maxEntriesLocalHeap 설정은 사용 할 수 없음.
maxBytesLocalOffHeap: 로컬 VM의 최대 offHeap 사용량을 설정 
maxBytesLocalDisk: maxBytesLocalHeap에 설정된 캐시 사용 이후에 대한 디스크 스토리지의 한계를 설정


주말에 식사 도중 처제가 물었다
"형부 팀장이죠?"
옆에 있던 아내가 대답을 가로챘다.
"응! 꼰대야! 젊은 꼰대!"

음...나는 아니라고 생각하는데 나의 생각을 잘 알고있는 아내가 그렇게 말한다면 꼰대인가 보다.

꼰대가 되지 않으려면 어떻게 해야 할까?

1. 내가 틀렸을지도 모른다.
2. 내가 바꿀 수 있는 사람은 없다.
3. 그때는 맞고 지금은 틀리다.
4. 말하지 말고 들어라. 답하지 말고 물어라.
5. 존경은 권리가 아니고 성취다.
- 출처:어쩌다어른

"자유롭게 의견을 얘기하라고 해놓고, 나중에 보면 내가 먼저 답을 제시했다."
- 「답정너」 는 꼰대의 시작이라고 생각한다.

늘 조심하고 한번 더 생각하자.

언제나 그렇듯 python  은 예제 소스코드로 보는 것이 이해하기 쉽다. 

import socket
import struct

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
addr = ('192.168.101.118', 2123)
s.connect(addr)

# header
magicCode = 1234
pType = 12
pVer = 1

# body 
userIp = bytes('192.168.3.101', encoding='UTF-8')
url = bytes('https://abc.com/def.jsp?param1=test¶m2=ABC', encoding='UTF-8')
urlLen = len('https://abc.com/def.jsp?param1=test¶m2=ABC')
userId = bytes('yhkim', encoding='UTF-8')
userIdLen = len('yhkim')

values = (magicCode, pType, pVer, userIp, urlLen, url, userIdLen, userId)
fmt = '>I I h 16s I {}s I {}s'.format(urlLen, userIdLen)
packer = struct.Struct(fmt)
sendData = packer.pack(*values)

try:
    s.sendall(sendData)
finally:
    s.close()

socket 의 첫번째 argument 

 - socket.AF_INET: IPv4

 - socket.AF_INET6: IPv6 

 - socket.AF_UNIX: UNIX local 통신 


socket 의 두번째 argument

 - socket.SOCK_DGRAM: UDP 

 - socket.SOCK_STREAM: TCP 

 - 등등 (socket.SOCK_RAW, socket.SOCK_SEQPACKET, socket.SOCK_NONBLOCK ....)


인터넷에 보이는 많은 예제들은 단순하게 echo server/client 를 보여주기 때문에 

실제 업무에서 사용하기에는 문제(?)가 있다. 

실제 업무에서는 보통 프로토콜을 정의해서 사용하기 때문이다. 

 - header 몇 byte, body 몇 byte, body 에서 첫번째 16byte는 사용자 IP... 뭐 이런식으로 


위 예제에서 사용한 프로토콜은 아래와 같다. 

header >  매직코드: BIN(4), 프로토콜 타입: UINT(4), 통신버전: UINT(2)

body > 사용자 IP: CHAR(16), URL길이: UINT(4), URL: CHAR(X), 아이디길이: UINT(4), 아이디:CHAR(X)

python 은 바이너리를 쉽게 packing, unpacking 할 수 있는 struct 라는 녀석을 제공한다. 

struct 라는 녀석에 대해서는 다음시간에 자세히 알아보기로 하고 일단 쉽게 생각하면 format에 맞춰서 변수들을 담아놓고 

한번에 바이너리로 처리할 수 있게 도와주는 녀석이라고 생각하면 될 것 같다...

(다음에 자세히 알아볼 때 ... 이게 아니라면 수정하자..)

struct 에서 사용하는 format 을 C 언어와 비교해서 표로 보면 이렇다.

소스코드에서 사용한 것을 보면 아래와 같다.

>I I h 16s I {}s I {}s'.format(urlLen, userIdLen)

앞에 '>'는 big-endian 을 나타내며 순서대로 포맷팅을 해 주는 것이다. 

16s 는 16 byte 문자열을 표현한다. 

Java의 ByteBuffer의 allocate 처럼 직관적이며 편하게 사용할 수 있다.

Test용 Client 를 만들 때 python 으로 만들면 이렇게 쉽고 간단하고 간결하게 만들 수 있다.

'IT > Python' 카테고리의 다른 글

Python - 예외 처리  (0) 2018.09.18
Python - Socket(Client) with struct (쉽게 테스트용 Client 만들기)  (0) 2018.09.11
Python - I/O, File I/O, pickle  (0) 2018.09.10
Python - Class  (0) 2018.09.07
Python - 자료구조  (0) 2018.08.30
Python - 함수  (0) 2018.08.27

이전글 : 2018/09/06 - [IT/Web] - Spring Boot + Spring Security + MyBatis + Vue.js

Spring Boot 를 start project 로 기본 생성하면 context-path 가 없다. 

http://localhost:8080/ 으로 접속하면 초기 화면이 나오게 된다. 

하나의 물리적 서버에 하나의 Tomcat Instance 와 하나의 Web Application 을 띄우는 것이 가장 좋겠으나 

하나의 Tomcat Instance 에 여러개의 Web Application 을 context name 으로 구분하여 띄우는 경우도 많다. 

이번에는 Spring Boot 에 context-path 를 설정하는 방법과 

그리고 Vue.js 를 BootStrap 을 이용해서 그럴싸하게 만들어 연동(?) 하는 방법을 정리 해 본다. 

우선 context-path 를 설정하는 것은 너무나 쉽다. 

src/main/resources/application.properties 에 아래와 같이 한줄을 추가 하면 된다.

server.servlet.context-path=/yhkim 

이렇게 하면 이제부터 http://localhost:8080/yhkim 으로 접속해야 초기 화면이 나오게 된다.

server 의 port 도 바꿔보자. 

server.port=80

이렇게 한줄 추가하면 http://localhost/yhkim 으로 접속 된다. 

그 외에도 아주 많은 설정들을 할 수 있으며 spring boot 에서 제공하는 여러 option 은 https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html 에서 확인 가능 하다. 


vue.js & bootstrap 은 찾아보면 예제가 정말 많다. 

https://bootstrap-vue.js.org/ 을 통해 하나씩 해봐도 되고 나는 잘 만들어진 template 을 써보려 한다. 

AdminLTE 이라는 괜찮은 Template 이 있어서 download 후 나의 frontend 쪽에 포팅해 보았다. 

여기서 주의해야 할 점은 context-path를 설정했기 때문에 frontend 인 vue project 에서도 context-path를 적용해야 한다는 점이다. 

vue-router 의 index.js 에 

const contextRoot = '/yhkim' 을 추가 하고 

routes 각각의 path 앞에 contextRoot 를 추가 해 준다. 

그리고 frontend/config/index.js 의 build 부분에 있는 assetsPublicPath 를 '/yhkim/' 으로 설정해 준다.  <-- 이부분을 하지 않으면 build된(webpack으로 bundling 된 css 와 js) 파일 안에 .css, .js, image 파일들의 경로가 맞지 않아 제대로 나오지 않을 수 있으니 주의 해야 한다


최종적인 화면은 이렇다. 

이제 각각의 화면에 맞는 Vue 를 만들고 그럴듯한 Web Application 을 작성하는 일만 남았다. 



#카니예웨스트 는 미국의 래퍼이자 프로듀서이자 패션디자이너이다.

그가 아디다스와 콜라보를 하면서 이지 시리즈가 시작되었다.

국내에서는 정가로 구하기 힘든 #이지부스트 의 종류는 참 다양하다.

 그 중 그나마 가장 대중적인것은 350일것이다.
그리고 인기가 좋은 이지부스트 700 이 국내에서 첫 라플을 시작했다.

오늘 #아디다스에 접속해보니 대기자가 좀 있었고 기다리면 #래플 신청을 할 수 있었다.


#나이키 드르우 처럼 랜덤 당첨이다
물론 나는 지금껏 단 한번도 드로우나 래플에서 당첨 된 적 없다..ㅜㅜ

도대체 누가 되는건지 모르겠다.

이번에는 꼭!!  #당첨 되길!!!


나이키의 에어맥스, 조던 시리즈와 더불어 아디다스의 이지 시리즈는 참...갖고 싶다


American Standard Code for Information Interchange - 줄여서 ASCII 

즉 1960년대 미국에서 정의한 표준화한 부호체계이다.


컴퓨터의 기본 저장 단위는 byte 이며 1byte 는 8bit 이다. 

1byte 에는 2의 8승인 256개의 값을 저장할 수 있는데 ASCII 코드는 7bit 즉 128개의 고유 값만 사용한다. 

1bit 는 Parity Bit 로 사용한다. 


다른 언어를 표현하기에는 7bit 로는 부족했기 때문에 8bit로 확장한 ASCII 코드가 나왔고 이것을 ANSI 코드 이다. 


하지만 여전히 한국, 중국, 일본과 같은 문자를 표현하기에는 제한이 있었다.

그래서 용량을 크게 확장한 2byte 기반의 UNICODE가 등장한다. (2의 16승인 65536개의 고유값 표현 가능)


UNICODE 코드 포인트를 8bit 숫자의 집합으로 나타내는 것이 UTF-8 이다. 


I/O 

앞으로 해도 이효리 뒤로 해도 이효리 이렇게 reverse 해도 같은 문장을 #plaindrome 이라고 부른다. 

plaindrome 을 검사하는 연습문제를 만들어 보자. 매우 간단하다.

import re

def reverse(text):
    return text[::-1]

def is_plaindrome(text):
    return text == reverse(text)

something = input('enter text:')

regexp = '\W'
something = re.sub(regexp, '', something)
something = something.upper()
print(something)

if is_plaindrome(something):
    print('Yes, it is a plaindrome')
else:
    print('No, it is not a plaindrome')
Rise to vote, sir. 과 같은 문장은 plaindrome 이다. 
정확한 plaindrome 을 확인하기 위해 정규식을 이용하여 영문/숫자가 아닌 경우 제거 하고 모두 대문자로 변경한다. 

File I/O 는 정말 쉽다. 
python 은 늘 예제 코드를 통해 확인하는 것이 배우기에 쉬운것 같다.
text = 'Programming is fun ' \
       'When the work is dome ' \
       'if you wann make your work also fun:' \
       'use Python!' \
       '...'

print('---open file for write')
f = open('test.txt', 'w')
print('---and write text')
f.write(text)

print('---open file for read')
f = open('test.txt', 'r')
while True:
    line = f.readline()
    if len(line) == 0:
        break
    print(line)
print('---file close')
f.close()
쉽다. open (w:write mode, r:read mode, b:binary mode.... help(open)을 통해 자세한 내용을 확인할 수 있다.) 하고 읽고, 쓰고, close 하면된다. 


pickle 
python 의 객체를 File 로 저장하고 다시 읽을 수 있다. 역시 예제 코드를 통해 확인해 보자.
import pickle

shoplistfile = 'shoplist.data'
shoplist = ['apple', 'mango', 'carrot']

f = open(shoplistfile, 'wb')
pickle.dump(shoplist, f)
f.close()

del shoplist

f = open(shoplistfile, 'rb')
storedlist = pickle.load(f)
print(storedlist)
f.close()



'IT > Python' 카테고리의 다른 글

Python - 예외 처리  (0) 2018.09.18
Python - Socket(Client) with struct (쉽게 테스트용 Client 만들기)  (0) 2018.09.11
Python - I/O, File I/O, pickle  (0) 2018.09.10
Python - Class  (0) 2018.09.07
Python - 자료구조  (0) 2018.08.30
Python - 함수  (0) 2018.08.27
유혹하는 글쓰기
국내도서
저자 : 스티븐 킹(Stephen King) / 김진준역
출판 : 김영사 2017.12.11
상세보기

제목이 참 마음에 들었다.

사람은 누구나 상대방을 유혹할 수 있는 매력을 가지고 있다고 믿고 있다.
그런데 '글' 로 누군가를 유혹할 수 있다는 것은 엄청난 재능이라고 생각한다. 
#쇼생크탈출 #미저리 등 많은 인기를 얻은 #작가 #스티븐킹 본인의 이야기를 풀어 놓은 책이다. 

#작가지망생 이나 글을 잘 쓰고 싶은 사람이라면 한번쯤 읽어볼만 하다.

솔직히 책을 읽으면서 엄청 매력적이라고 느끼진 못했다. 
하지만 그가 하려는 말은 아주 간단 명료했다.
'사족을 붙이지 마라' . 
원문과 비교하면서 불필요한 표현을 제거하고 마음에 들지 않는 표현은 바꿔주는 예시를 보여주곤 한다. 그런데... 그가 불필요하다고 이야기 하면서 제거한 여러 표현들이 나에겐 그리 나빠 보이지 않았다. 
나는 이과생이라서 그런가 그의 설명이 그리 확 와닿지는 않았다. ;; 

그래도 그가 이야기한 것은 #유시민 작가가 이야기한 
'불필요한 조사와 접속사를 쓰지 않는 것' 과 일맥상통한다. 

원래 잘 모르는 사람들이 말을 어렵게 하고 길게 한다고 한다. 
그래서 이번 독서 후기는 이렇게 짧게 마무리 한다.



'독서' 카테고리의 다른 글

유혹하는 글쓰기 - 2019.09.10  (0) 2018.09.10
자기신뢰 - 2018.08.22  (0) 2018.08.30
최고의 공부 - 2015.01.14  (0) 2015.01.14
그릿 - 2015.01.02  (0) 2015.01.02
서른과 마흔사이 - 2014.12.09  (0) 2014.12.09
트렌드 코리아 - 2014.11.28  (0) 2014.11.28

스마트 TV 구입 후 그 유명한(?) 넷플릭스를 가입하고 한달 무료 시청을 했다. 시간가는줄 모르고 어느새 한달이 훌쩍 지나가 버렸다.

#넷플릭스 덕분에 #breakingbad 라는 기가막힌 드라마도 알게되었고
IPTV는 거의 보지 않고 있다.

한달 사용소감은 "대만족" 이다.

사실 더 사용하고 싶지만 IPTV 3년약정노예이기 때문에 가격의 압박이 좀..있어서 아쉽지만 해지한다.

1. 넷플릭스에 로그인 하고 '고객센터' 클릭


2. 밑으로 내려서 '멤버십 해지' 클릭


3. 끝!!!


간단하다..

이제 아내아이디로 새롭게 가입해서 봐야겟다..ㅋㅋ

#브레이킹베드 아직 다 안봤으니까..ㅎ

인스타그램을 하면서 지난 몇년간 일상, 여행등의 포스팅은 블로그에 남기지 않았었다.
이제 다시 시작해 보려 한다.
작년에 #포천아트밸리 와 이곳저곳을 다니면서 포천은 참 볼게 많구나.. 라는 생각을 했었다.
올해 포천은 #어메이징파크 !!
이름처럼 어메이징 했다. 
5살 딸아이와 함께하기에 정말 좋았다. 
10시 30분에 집에서 출발해서 12시 30분에 도착!!!  멀긴 멀구나..^^;
늦은시간이라 주차요원이 주차할곳이 없다고 밑에 주차하고 셔틀(?)버스를 타고 올라가란다..
유모차가 있어서 안되겠다고 말하니까 일단 그럼 올라오란다. 올라가서 어슬렁거리다가 빈곳을 찾아 주차했다. (이 방법을 이용하세요)

날씨가 참 좋았다 시원하고 햇살도 좋고 딸래미 기분도 좋고 ㅎㅎ

첫번째 코스는 엄청긴 흔들다리 ^^ 
역시 겁이라곤 1도 없는 율이는 뛰어간다

300m정도 되는 #흔들다리 를 2번 왕복하니 출출했던 배가 밥달라고 신호를 보낸다.

어메이징파크 안에는 푸드코트가 있다.
메뉴가 그리 많지 않지만 배가 고파서였을까 맛은 참 괜찮았다. (옛날맛 떡볶이 추천)

밥을먹고 들어간 곳은 #과학관 #포천어메이징파크과학관 이다.
이곳은 물줄기를 가로지르는 큰 자동그네가 유명한곳이다.

엄청 재밌다!!! 엄마도 타고 아빠도 타고 율이는 2번이나 탔다ㅋㅋ
과학관은 3층까지 있는데 대부분 아래 사진과 같은것들로 이루어져 있다. 2층까지 올라갔다면 굳이 3층은 올라가지 않아도 될듯

나와서 바로 간곳은 #히든브릿지 !!!
여기 참 좋다!!!  진짜 자연속으로 숲속으로 들어간 기분을 만끼할 수 있다.

나무들 사이로 브릿지가 이어져있고 맑은 공기를 마시면서 정말 기분 좋은 경치들을 보면서 #힐링 할 수 있다.
파노라마샷도 멋있게 나온다


히든브릿지에서 나와 위로 조금 올라가면 #지피라인 을 탈 수 있다.
자유이용권을 끊었음에도 #짚라인 은 인당 1만원의 추가요금을 받는다.

키가 130  이상이어야 가능하다는 말이 있으나 부모가 함께 탑승하고 잘 잡아주면 같이 탈수 있다. 엄청 신났다고 한다.  아빠는 둘째를 안고 있어서 ... 다음에 타기로..


이제 조금 더 올라가면 아주 큰 기계식(?) 물줄기를 뿜어내는 분수가 나온다.

분수 덕분에 만들어진 무지개를 보며 신난 율이 ^^
분수 바로 옆엔 끝없는 무지게 계단이 올라와보라고 속삭인다

올라가야지!!! ㅋ...ㅋ 
아빠와함께 힘을 내서 열심히 올라간 기특한 율이!!  생각보다 별로 그렇게 높지 않다.
올라가면 #행복의종 과 그네, 평상등이 있다.
평상에 누워 시원한 바람을 느끼면서 하늘을 보니 .. 이것이 진정한 무릉도원이구나..라는 생각이 들었다.

기분좋게 쉬고 다시 내려가 본다. 
그사이 엄마는 연이를 재웠다. ^^

이제 밑으로 조금더 내려가면 3개정도 놀이터가 나온다. 
첫번째 놀이터에는 사랑의 의자가 있다. 사랑하는 딸과 타본다ㅎ



두번째는 그냥 쉬는곳 이라서 패스하고 맨 밑으로 내려와서 다람쥐통을 신나게 굴린다.!!

이제...거의 다 왔어...
아직 하나 더 남았어.. 
#바람개비동산 을 마지막으로 어메이징파크를 마무리 한다!

하트 뿅뿅 날려주는 핑크공주 ♥ 스릉흔드~

자!!!  이제 끝!!!! ????  
.
.
.
.

아...이제 주차 해 놓은 곳으로 올라가야지...

집에 돌아가면서 들렸던 #포천맛집 #밀천지칼국수 도 적극 추천한다!!  
너무 맛있어서 사진을 못찍었다!!  #포천JMT


하얗게 불태웠어ㅋㅋㅋ


어메이징한 하루 였다 ^^

class Person:
  salary = 0
  def __init__(self, name):
    self.name = name
  def say_hi(self):
    print('Hello, my name is', self.name)
  def say_salary(cls):
    print('My salary is', cls.salary)
    cls.salary += 1

p1 = Person('Younghoi')
p1.say_hi()
Persin.say_salary()

p2 = Person('Kim')
p2.say_hi()
Person.say_salary()

init method 는 예상처럼 객체/인스턴스가 생성될때 호출되는 method이다.

재미있는것은 클래스변수와 객체 변수가 있다는 것이다.

클래스 변수인 salary는 서로 다른 객체 p1과 p2가 서로 공유할 수 있다.

Person.salary 라고 접근하는 클래스 변수는 self.class.salary 로도 접근 가능하다.

객체지향프로그램의 여러 장점 중 가장 먼저 쉽게 떠올릴 수 있는 "상속" 은 어떻게 표현 할까?


class SchoolMember:
    def __init__(self, name, age):
        self.name = name;
        self.age = age;
        print('(Initialized SchoolMember: ', self.name)
    def tell(self):
        print('Name:{}, Aage:{}'.format(self.name, self.age))

class Teacher(SchoolMember):
    def __init__(self, name, age, salary):
        SchoolMember.__init__(self, name, age)
        self.salary = salary
        print('(Initialized Teacher: {}'.format(self.name))

    def tell(self):
        SchoolMember.tell(self)
        print('Salary', self.salary)

class Student(SchoolMember):
    def __init__(self, name, age, marks):
        SchoolMember.__init__(self, name, age)
        self.marks = marks
        print('(Initialized Student: {}'.format(self.name))

    def tell(self):
        SchoolMember.tell(self)
        print('Marks: ', self.marks)

t = Teacher('Mrs. Lee', 40, 30000)
s = Student('Kim', 25, 90)

members = [t, s]
for member in members:
    member.tell()

'IT > Python' 카테고리의 다른 글

Python - Socket(Client) with struct (쉽게 테스트용 Client 만들기)  (0) 2018.09.11
Python - I/O, File I/O, pickle  (0) 2018.09.10
Python - Class  (0) 2018.09.07
Python - 자료구조  (0) 2018.08.30
Python - 함수  (0) 2018.08.27
Python - 기본문법  (0) 2018.08.27

+ Recent posts

티스토리 툴바