2018년 6월 13일 수요일

mahout 알고리즘

아파치 머하웃(Apache Mahout)은 아파치 소프트웨어 재단의 한 프로젝트로서 분산처리가 가능하고 확장성을 가진 기계학습용 라이브러리이다. 맵리듀스를 이용하는 아파치 하둡위에 적용되며[1][2] 비슷한 특성을 가진 데이터들을 분류하고 정의하는 작업 및 협업 필터링 분야에 집중한다.
머하웃(Mahout)의 사전적 의미는 코끼리를 부리는 사람을 말한다.



스터디프로젝트에 구독한 강좌를 기반으로 추천해주는 시스템을 구현하고 싶어서 추천 알고리즘을 찾는도중 아파치의 머하웃 알고리즘을 찾았다.

3번 예제를 실행해보았지만 아직 감이 잡히지 않으므로 공부 더 더ㅓ더더더
**사용자/아이템이름/선호값 데이터를 바탕으로 추천을 해주는 것 같음...,.,.


++++++++++++++++
6/30
3번 예제에서 아이템 기반 추천을 해보았다. 실행 결과 모든 아이템들의 유사성?선호도? 값이 출력된다. 데이터가 누적이 될 수록 1번 아이템을 선택했을 때 추천해주는 아이템이 달라지겠지만
결국 1번 아이템을 선택했을 때 추천해주는 아이템은 모든 사용자에게 똑같이 나타나는 것 같음.  >>사용자 개별의 추천은 아닌 것 같지만 사용자 기반 추천 알고리즘은 이해하지 못했으므로 일단 이것을 프로젝트에 적용해보고 추후에 생각해 볼 것


7/8
ItemRecommend를 실행하면 리스트(recommendations)에 값들이 저장됨
우리 프로젝트에서는 코스를 클릭하였을 때 밑에 추천 코스들을 표시할 계획이므로 코스번호를 받아서 ItemRecommend를 실행해서 특정 코스번호와 연관된 코스 5개정도 뽑아서 코스 정보 보여주게 하면 될 듯?



7/29
프로젝트에 적용하여 코스를 클릭하였을 때 밑에 추천 코스들을 뜨게하였다.
기본적으로 5개를 보여주도록 하였고, 현재는 데이터를 예제로 썼기 때문에 프로젝트에 적용하였을 때 추천해주는 번호가 실제 강좌에 없는 것도 있기 때문에 5개가 전부 뜨지않는다.

todo
- 추천방식 이대로할 것인지?
- 코스이름을 가져오는 방식 너무 코드가 더러움 (개선해야함)
- 엑셀의 기록된 데이터를 기준으로 추천이 진행되므로 
디비의 데이터를 엑셀로 정리 or u.data 파일에 저장하고 엑셀로 변환
(데이터 갱신 주기 설정 - 스케쥴러도 좋은듯... ) 



예)
사용자들의 101~ 106번 아이템에 대한 관계 >>>이 데이터를 통해 추천값 계산
아이템 번호 / 아이템 번호 / 아이템에 따른 대상 아이템에 대한 선호값 


1.0에 높을 수록 선호도가 높은 것 같음
>>익명의 사용자가 101번을 선택하면 제일 1.0에 가까운 102번을 추천
(데이터가 적어서 1.0가 표시됨. 예제의 큰 데이터에서는 제일 높은 값이 0.5였다!!)


8/15
현재 csv를 기준으로 추천을 해주므로,
데이터베이스의 한 테이블을 기준으로 그 테이블 내용을 .data나 .csv 파일로 뽑아서
그 파일을 읽어 추천을 적용하면 될듯..
기준 테이블 고민하기: 구독 or 수강평
구독으로 설정하면 선호도를 무조건 5로 놔야함
수강평으로 설정하면... 수강평 등록할 때 한사람당 한번씩만 해야하고, 각 회원 번호를  새 테이블에 넣어야할듯..??


*데이터베이스 테이블의 데이터를 csv 형태로 내보내기
https://gist.github.com/gaerae/6219678

8/19
사용자가 수강평을 남길때 마다 새 테이블에 사용자번호 / 코스번호 / 점수
를 입력하게 하였고, 이 테이블을 특정 폴더에 .csv 파일로 뽑았다.
이 파일을 통해 추천을 해주도록 하였고, 파일의 갱신은 관리자계정이 버튼을 통해
갱신을 하도록 하였다.



사용자 기반 알고리즘과 선호도계산 로직에 대해 좀 더 알아보면 좋을 것같다..



<예제>
1. 어려운 예제
https://github.com/jensfischerhh/spring-boot-starter-recommender
2. 스프링에 적용시킨 추천 알고리즘
https://github.com/hoho0443/recommend_mahout_postgresql_spring
3. 간단한 실습
http://over153cm.tistory.com/entry/%EB%A8%B8%ED%95%98%EC%9B%83-recommender-%EC%8B%A4%EC%8A%B5%ED%95%98%EA%B8%B0?category=459417


<머하웃 기본개념>
http://blog.naver.com/PostView.nhn?blogId=koys007&logNo=220754318580&parentCategoryNo=&categoryNo=&viewDate=&isShowPopularPosts=false&from=postView

2018년 5월 22일 화요일

MySQL Error Code 1175 관련

Error Code: 1175. You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column To disable safe mode, toggle the option in Preferences -> SQL Editor and reconnect.
update safe모드로 설정되있어서 업데이트시 키 컬럼을 사용하지 않으면 업데이트 할수 없다... 라는 것 같다



방법

SET SQL_SAFE_UPDATES =0;
라는 쿼리문을 입력한다.

or 

워크벤치의 Edit - Preferences 에서 빨간 원을 체크 해제 하면 된다.



2018년 5월 17일 목요일

mongodb 설치

예전에 설치했던 mongodb가 접속이 안되서 새로 설치하였다

환경변수를 등록하고

C:\data\db
이 경로에 폴더를 설치해줘야한다 


cmd로 mongod를 입력하고

이때 이 창은 그대로 두고 새 cmd창을 열어 mongo 입력하면 접속완료



참고
http://solarisailab.com/archives/1605

2018년 4월 21일 토요일

게시판 스크립트 방지 관련

포폴로 제출한 홈페이지를 잠깐 들어가봤는데 내가 설정하지 않은 alert창이 떠서 당황하였다.
인사담당자님께서 테스트하신 것같았는데 글 제목들은 공백이었고 내가 입력한적이 없는 alert창이 자꾸 발생하였다. 
어디서 자꾸 발생하나 개발자도구로 보니.....글 제목을 스크립트로 입력하신거였다...

<script>alert('Popup');</script>

전혀 생각하진 못한 문제였다...ㅠㅠㅠ

글 내용부분은 네이버 스마트에디터를 사용해서 스크립트가 방지되는 것같았다. 
글 제목엔 <script>alert('Popup');</script>을 써서 저장하면 해당게시판의 페이지에 접속할 때마다 알람이 발생한다!!! 


* XXS(Cross-site Scripting란?
SQL injection과 함께 웹 상에서 가장 기초적인 취약점 공격 방법의 일종으로, 악의적인 사용자가 공격하려는 사이트에 스크립트를 넣는 기법을 말한다. 공격에 성공하면 사이트에 접속한 사용자는 삽입된 코드를 실행하게 되며, 보통 의도치 않은 행동을 수행시키거나 쿠키나 세션 토큰 등의 민감한 정보를 탈취한다.

크로스 사이트 스크립팅이란 이름 답게, 자바스크립트를 사용하여 공격하는 경우가 많다. 공격 방법이 단순하고 가장 기초적이지만, 많은 웹사이트들이 XSS에 대한 방어 조치를 해두지 않아 공격을 받는 경우가 많다. 여러 사용자가 접근 가능한 게시판 등에 코드를 삽입하는 경우도 많으며, 경우에 따라서는 메일과 같은 매체를 통해서도 전파된다. 

물론, HTML을 사용하는 것이기 때문에, Text-Only 게시판이나, BBCode를 이용하는 위키위키 등에서는 XSS가 발생할 일은 없다. 단, 나무위키의 경우 {{{#!html HTML}}} 을 이용해서 HTML 태그를 사용할 수 있으므로 취약점이 있을 수도 있다. 그래서 나무위키 초반에는 스크립트 태그와 이벤트 속성도 막지 않았다. 물론, 이는 후에 대부분 수정되었다.

주로 CSRF를 하기 위해서 사용되기 때문에 종종 CSRF와 혼동되는 경우가 있으나, XSS는 자바스크립트를 실행시키는 것이고, CSRF는 특정한 행동을 시키는 것이므로 다르다.




해결방법은 좀 더 찾아보고 적용해야할 듯...ㅠㅠ 


http://forest71.tistory.com/16 (게시판 스크립트 방지관련 글)

https://blog.naver.com/powerlee12/40051367646 (xss에 대해서)

https://namu.wiki/w/XSS

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
lucy xss servlet filter를 적용 테스트 중 ...

2018년 3월 24일 토요일

modelandview란?

ModelAndView

org.springframework.web.servelt 패키지에 속해있는 클래스로 컨트롤러의 처리결과를 보여줄 뷰와 전달할 값을 저장할 용도로 쓰인다.
ModelAndView클래스를 보면 다음과 같이 구성되어 있다.
package org.springframework.web.servlet;

import java.util.Map;
import org.springframework.http.HttpStatus;
import org.springframework.ui.ModelMap;
import org.springframework.util.CollectionUtils;

public class ModelAndView {
    private Object view;
    private ModelMap model;
    private HttpStatus status;
    private boolean cleared = false;

    public ModelAndView() {
    }

    public ModelAndView(String viewName) {
        this.view = viewName;
    }

    public ModelAndView(View view) {
        this.view = view;
    }

    public ModelAndView(String viewName, Map<String, ?> model) {
        this.view = viewName;
        if (model != null) {
            this.getModelMap().addAllAttributes(model);
        }

    }

    public ModelAndView(View view, Map<String, ?> model) {
        this.view = view;
        if (model != null) {
            this.getModelMap().addAllAttributes(model);
        }

    }

    public ModelAndView(String viewName, HttpStatus status) {
        this.view = viewName;
        this.status = status;
    }

    public ModelAndView(String viewName, Map<String, ?> model, HttpStatus status) {
        this.view = viewName;
        if (model != null) {
            this.getModelMap().addAllAttributes(model);
        }

        this.status = status;
    }

    public ModelAndView(String viewName, String modelName, Object modelObject) {
        this.view = viewName;
        this.addObject(modelName, modelObject);
    }

    public ModelAndView(View view, String modelName, Object modelObject) {
        this.view = view;
        this.addObject(modelName, modelObject);
    }

    public void setViewName(String viewName) {
        this.view = viewName;
    }

    public String getViewName() {
        return this.view instanceof String ? (String)this.view : null;
    }

    public void setView(View view) {
        this.view = view;
    }

    public View getView() {
        return this.view instanceof View ? (View)this.view : null;
    }

    public boolean hasView() {
        return this.view != null;
    }

    public boolean isReference() {
        return this.view instanceof String;
    }

    protected Map<String, Object> getModelInternal() {
        return this.model;
    }

    public ModelMap getModelMap() {
        if (this.model == null) {
            this.model = new ModelMap();
        }

        return this.model;
    }

    public Map<String, Object> getModel() {
        return this.getModelMap();
    }

    public void setStatus(HttpStatus status) {
        this.status = status;
    }

    public HttpStatus getStatus() {
        return this.status;
    }

    public ModelAndView addObject(String attributeName, Object attributeValue) {
        this.getModelMap().addAttribute(attributeName, attributeValue);
        return this;
    }

    public ModelAndView addObject(Object attributeValue) {
        this.getModelMap().addAttribute(attributeValue);
        return this;
    }

    public ModelAndView addAllObjects(Map<String, ?> modelMap) {
        this.getModelMap().addAllAttributes(modelMap);
        return this;
    }

    public void clear() {
        this.view = null;
        this.model = null;
        this.cleared = true;
    }

    public boolean isEmpty() {
        return this.view == null && CollectionUtils.isEmpty(this.model);
    }

    public boolean wasCleared() {
        return this.cleared && this.isEmpty();
    }

    public String toString() {
        StringBuilder sb = new StringBuilder("ModelAndView: ");
        if (this.isReference()) {
            sb.append("reference to view with name '").append(this.view).append("'");
        } else {
            sb.append("materialized View is [").append(this.view).append(']');
        }

        sb.append("; model is ").append(this.model);
        return sb.toString();
    }
}
다음과 같이 map에 데이터를 담거나 직접 값을 전달해 줄 수있다.
@RequestMapping(value="list")
  public ModelAndView list(@RequestParam(defaultValue="title") String searchOption,
    @RequestParam(defaultValue="") String keyword,
    @RequestParam(defaultValue="1") int curPage) throws Exception{

  //전달할 정보 가져오기
  int count = adminService.countboard(searchOption, keyword);
  BoardPage boardPage = new BoardPage(count, curPage);
  int start = boardPage.getPageBegin();
  int end = boardPage.getPageEnd();
  List<WebBoard> list = adminService.Viewlist(start, end, searchOption, keyword);

  //전달할 정보 map에 담기
  Map<String, Object> map = new HashMap<String, Object>();
  map.put("list", list);
  map.put("searchOption", searchOption);
  map.put("keyword", keyword);
  map.put("boardPage", boardPage);


  ModelAndView mav = new ModelAndView();

  //ModelAndView 객체에 전달할 정보(map)과 뷰 설정하기
  mav.addObject("map", map);
  //map에 넣지않고 직접 전달해도 된다.
  mav.addObject("count",count);
  mav.setViewName("/admin/adminmode");

  return mav;
  }
ModelAndView 외에도 다양하게 컨트롤러의 리턴타입을 정할 수 있음.
@ReqeustMapping("/list/")
  public String list(Model model){

    int count = adminService.countboard();

    model.addAttribute("count", count);

    return "/admin/adminmode"
  }

2018년 3월 4일 일요일

restcontroller

REST는 'Representational State Transfer' 의 약어로 하나의 URI는 하나의 고유한 Resource를 대표하도록
설계된다는 개념이다.
이 말은 다른 말로 'URI와 HTTP메소드를 이용해 객체화된 서비스에 접근한다고 말하는 것'이 라고 말할 수도 있는데
이 편이 더 쉽게 이해할 수도 있다.

REST API는 외부에서 특정 URI를 통해서 사용자가 원하는 정보를 제공하는 방식이다.
최근에 Open API에서 많이 사용되면서 REST 방식을 제공되는 외부 연결 URI를 REST API라고 하고,
REST 방식의 서비스 제공이 가능한 것을 'Restful' 하다고 표현한다.






예제( 다른 방법도 있는것같음 ) 
http://doublesprogramming.tistory.com/105

+++++