프로그래밍/+a (13)

728x90

 멤버 다음은 lol api 사용해서 정보 가져와서 뿌리는거 하려고 했는데 같은 조원 분이 게시판이 중요한 것이라고 하셔서 다음 단계는 게시판으로 결정했다.


1차 일지 - [ 설정 ], [ 회원가입 ]
2차 일지 - [ 로그인 ]
3차 일지 - [ index에서 로그인 상태에 따라 display none/block 제어하고 p태그 내용 바꾸기 ], [ 로그아웃 ]
4차 일지 - [ header에 회원 아이디, 회원정보/로그아웃 링크 or 비회원 문구 추가 후 로그인 상태에 따라 display none/block 제어 ], [ 일반 회원 정보 확인 ]

5차 일지 - [ 일반 회원 정보 출력 변경하면서 관리자 로그인 시 회원 정보 출력 추가 ], [ 일반 회원 비밀번호 변경 ]

 

1. 회원 정보 출력

 일반 회원 정보 출력을 관리자랑 같이 사용하려면 변경이 필요할 것 같아서 조금 변경하다보니 거의 다 변경하게 되었다..
 MemberController는 변경점이 없어서 빼고 작성했다.
  • memberInfo.jsp
더보기
  • body
<p>현재 로그인 중인 id: ${loginId}</p>

<br>
<div id="info">
</div>

 

  • script
$(()=>{
	let loginId = '<%=(String)session.getAttribute("loginId")%>';
	
	// ============================ 회원 정보 관련 ================================
	$.ajax({
        method: 'get',
        url: 'memberInfo',
    }).done(function(res){
        console.log("res: ",res);
        const mList = res;

        if (loginId == "admin"){
            $("#info").html(" => 회원 목록");
            for (i=0; i<mList.length; i++){
                $("#info").append("<p>=================================</p>");
                $("#info").append("<p>이름: "+mList[i].mname+"</p>");
                $("#info").append("<p>이메일: "+mList[i].memail+"</p>");
                if (!(mList[i].gname <= 0)){
                    $("#info").append("<p>게임 닉네임: "+mList[i].gname+"</p>");
                }
                $("#info").append("<p>가입 날짜: "+mList[i].joinDate+"</p>");
                if (i==mList.length-1){
                    $("#info").append("<p>=================================</p>");
                }
            }

        }else{
            $("#info").html("<p>이름: "+mList[0].mname+"</p>");
            $("#info").append("<p>이메일: "+mList[0].memail+"</p>");
            if (!(mList[0].gname <= 0)){
                $("#info").append("<p>게임 닉네임: "+mList[0].gname+"</p>");
            }
            $("#info").append("<p>가입 날짜: "+mList[0].joinDate+"</p>");

            document.getElementById("infoPwCh").style.display = 'block';
        }

    }).fail((err,status)=>{
        console.log("err:", err);
        console.log("status:", status);
        $("#info").html("에러!").css('color','red');

    })
})
  • $(()=>{}): 페이지가 로딩되면 바로 실행하는 함수
  • 4일차에서는 모든 정보를 비밀번호를 받고나서 띄우게 했었는데, 비밀번호를 변경할 때만 비밀번호를 받도록 변경했다.
  • 세션에 저장된 아이디가 admin인 경우, 아닌 경우를 나누어 회원 정보 또는 회원 정보 리스트를 띄웠다. (조건으로 리스트의 길이로 줘도 괜찮았을 것 같다!)
  • gName인 게임 닉네임이 가장 큰 고난이었는데, 조건으로 gname >= 0, gname <= 0 둘 다 해봤는데(후자는 안 될 것을 알고 해봤다.) 널 값만 잘 띄우길래 포기하고 하나로 묶어 앞에 !(느낌표)를 적어 조건을 뒤집어줬다. 

 

  • MemberAsyController.java
더보기
@GetMapping("/member/memberInfo")
public ArrayList<MemberDto> memberInfo(HttpSession session) {
    log.info("==> GetMapping - memberInfo 요청 <==");
    String mId = session.getAttribute("loginId").toString();
    return mSer.memberInfo(mId);
}
  • 4일차에서 return 타입을 MemberDto로 했었는데, ArrayList<MemberDto>로 변경했다.

 

  • MemberService.java
더보기
public ArrayList<MemberDto> memberInfo(String mId) {
    log.info("===> mSer memberInfo 요청 <===");
    ArrayList<MemberDto> mList = new ArrayList<>();
    if (mId.equals("admin")) {
        mList = mDao.mListInfo(mId);
    }else {
        MemberDto mDto = mDao.memberInfo(mId);
        mList.add(mDto);
    }
    return mList;
}
  • MemberAsyController에서 return 타입을 MemberDto에서 ArrayList<MemberDto>로 변경함에 따라 해당 메소드도 return 타입을 동일하게 변경했다.
  • 매개변수로 받아온 id가 admin인지 아닌지에 따라 MemberDao의 메소드를 다르게 사용하여 mList에 담아서 그걸 return했다.

 

  • MemberDao (java, xml)
더보기
  • 일반 회원 메소드
MemberDto memberInfo(String mId);	// .java
<select id="memberInfo" parameterType="String">
    select mId, mName, mEmail, gName, joinDate from member where mId=#{param1}
</select>				// .xml

 

  • admin(관리자) 메소드
ArrayList<MemberDto> mListInfo(String admin);	// .java
<select id="mListInfo">
    select mId, mName, mEmail, gName, joinDate from member where mId!=#{param1}
</select>				// .xml

 

  • 결과
더보기
  • 일반 회원
회원 정보 출력 / 비밀번호 변경은 2번에서 후술.

 

  • admin(관리자)
회원 정보 출력

 

 

2. 일반 회원 비밀번호 변경

  • memberInfo.jsp
더보기
  • body
<div id="infoPwCh" style="display: none;">
    <p>비밀번호를 변경하시려면 현재 비밀번호를 입력하세요.</p>
    <input type="password" name="mPw" id="infoMPw" class="input" placeholder="비밀번호를 입력하세요" maxlength="20" autocapitalize="off">
    <div id="infoCh" style="display: none;"></div>
    <button type="button" class="btn btn-3 btn-3e" id="infoSubBtn" style="text-align: center;">비밀번호 확인</button>
</div>

<div id="changePw" style="display: none;">
    <input type="password" name="changeMPw" id="changeMPw" class="input" placeholder="변경할 비밀번호를 입력하세요" maxlength="20" autocapitalize="off">
    <input type="password" name="changeMPwRe" id="changeMPwRe" class="input" placeholder="변경할 비밀번호 재입력" maxlength="20" autocapitalize="off">
    <button type="button" class="btn btn-3 btn-3e" id="changeMPwBtn" style="text-align: center;">비밀번호 변경</button>
    <p id="changeAlert" style="display: none;"></p>
</div>

 

  • script
// ============================ 비밀번호 변경 관련1 ================================
$('#infoSubBtn').on('click',function(){			// 현재 비밀번호 확인
    let pw = $('#infoMPw').val();
    if (pw==''){
        $('#infoCh').html('비밀번호를 입력해주세요.').css('color','red');
        document.getElementById("infoCh").style.display = 'block';
        $('#infoMPw').focus();
        return;
    }
    document.getElementById("infoCh").style.display = 'none';
    let chPwSend = {mId:loginId, mPw:pw};
    $.ajax({
        methoed: "get",
        url: "pwCheck",
        data: chPwSend,
    }).done(function(res){
        if (!(res <= 0)){
            document.getElementById("changePw").style.display = 'block';
            document.getElementById("infoPwCh").style.display = 'none';
        }else{
            $('#infoCh').html('비밀번호가 틀렸습니다.').css('color','red');
            document.getElementById("infoCh").style.display = 'block';
        }
    }).fail((err,status)=>{
        console.log("err:", err);
        console.log("status:", status);
        document.getElementById("infoCh").style.display = 'block';
        $("#infoCh").html("비밀번호가 틀렸습니다.").css('color','red');

    })
})
// ============================ 비밀번호 변경 관련2 ================================
let usePw = false;

$('#changeMPwRe').on('click',function(){	// 비밀번호 체크
    let pw = $('#changeMPw').val();
    let chPw = $('#changeMPwRe').val();
    let con = document.getElementById("changeAlert");
    con.style.display = 'block';
    if (pw==''){
        $('#changeAlert').html("변경할 비밀번호를 먼저 입력해주세요.").css("color",'red');
        $('#changeMPw').focus();
        return;
    }
})
$('#changeMPwRe').blur(function(){
    let pw = $('#changeMPw').val();
    let chPw = $('#changeMPwRe').val();
    let con = document.getElementById("changeAlert");
    con.style.display = 'block';
    if (pw!='' && chPw==''){
        $('#changeAlert').html("비밀번호 확인 칸은 비워둘 수 없습니다.").css("color",'red');
        $('#changeMPwRe').focus();
        return;
    }
})
$('#changePw').on('keyup', function(){
    let pw = $('#changeMPw').val();
    let chPw = $('#changeMPwRe').val();
    let con = document.getElementById("changeAlert");
    con.style.display = 'block';
    if (pw!=''){
        if(pw.length<=20 && pw.length>=8){
            $('#changeAlert').html("");
        }else if(pw.length>20 || pw.length<8){
            $('#changeAlert').html("비밀번호의 길이는 8~20자 사이로 정해주세요.").css("color",'red');
            $('#changeMPw').focus();
            return;
        }
        if(chPw!='' && pw!=chPw){
            $('#changeAlert').html("비밀번호가 일치하지 않습니다.").css("color",'red');
            $('#changeMPwRe').focus();
        }else if(pw==chPw){
            usePw = true;
            con.style.display = 'none';
        }
            return;
    }
    usePw = false;
});			// 비밀번호 체크 끝


$('#changeMPwBtn').on('click',function(){	// 비밀번호 변경
    let changePw = $('#changeMPw').val();
    let changePwRe = $('#changeMPwRe').val();

    if (!usePw){
        document.getElementById("changeAlert").style.display = 'block';
        $('#changeAlert').html("비밀번호를 확인해주세요.").css("color",'red');
        return;
    }

    let changePwSend = {mId:loginId, changeMPw:changePw} 
    $.ajax({
        methoed: "get",
        url: "changeMPw",
        data: changePwSend,
    }).done(function(res){
        if (res == "ok"){
            alert("비밀번호를 성공적으로 변경했습니다.");
            document.getElementById('infoMPw').value = null;
            document.getElementById('changeMPw').value = null;
            document.getElementById('changeMPwRe').value = null;
            /* $('#infoMPw').value = null;
            $('#changeMPw').value = null;
            $('#changeMPwRe').value = null; */
            document.getElementById("changePw").style.display = 'none';
            document.getElementById("changeAlert").style.display = 'none';
            document.getElementById("infoPwCh").style.display = 'block';
        }else{
            $('#changeAlert').html('비밀번호를 변경하지 못했습니다.').css('color','red');
            document.getElementById("changeAlert").style.display = 'block';
        }
    }).fail((err,status)=>{
        console.log("err:", err);
        console.log("status:", status);
        document.getElementById("changeAlert").style.display = 'block';
        $("#changeAlert").html("비밀번호를 변경하지 못했습니다.").css('color','red');

    })

})

 새로운 비밀번호를 입력하고 또 그 비밀번호를 확인하는 것 때문에 스크립트가 길어졌지만 회원가입 할 때와 스크립트가 유사하기 때문에 마지막 스크립트만 시간이 걸렸다.

 

  • MemberAsyController.java
더보기
  • 현재 비밀번호 확인
@GetMapping("/member/pwCheck")
public String pwCheck(@RequestParam(name = "mId")String mId, @RequestParam(name = "mPw")String mPw) {
    log.info("==> GetMapping - pwCheck 요청 <==");
    MemberDto mDto = new MemberDto();
    mDto.setMId(mId);
    mDto.setMPw(mPw);
    if(mSer.login(mDto)) {
        return "ok";
    }
    return null;
}

 간단하게 id만 가져와서 로그인 할 때 사용했던 메소드를 사용하여 비밀번호가 맞는지 확인했다.

 

  • 새로운 비밀번호로 변경
@GetMapping("/member/changeMPw")
public String changeMPw(@RequestParam(name = "mId")String mId, @RequestParam(name="changeMPw")String changeMPw) {
    log.info("==> GetMapping - changeMPw 요청 <==");
    MemberDto mDto = new MemberDto();
    mDto.setMId(mId);
    mDto.setMPw(changeMPw);
    if(mSer.changeMPw(mDto)) {
        return "ok";
    }
    return "no";
}

 

  • MemberService.java
더보기
public boolean changeMPw(MemberDto mDto) {
    log.info("===> mSer changeMPw 요청 <===");
    BCryptPasswordEncoder pwEn = new BCryptPasswordEncoder();
    mDto.setMPw(pwEn.encode(mDto.getMPw()));
    if (mDao.changeMPw(mDto)) {
        return true;
    }
    return false;
}
  • 회원가입을 암호화해서 저장했고, 로그인할 때 암호화된 비밀번호만 비교했었으니 변경해서 저장할 때도 꼭 암호화를 해서 저장해주어야 한다!
  • 처음에 암호화를 안 하고 저장해버려서 로그인 할 수 없었다 ㅠ

 

  • MemberDao (java, xml)
더보기
  • .java
boolean changeMPw(MemberDto mDto);

 

  • .xml
<update id="changeMPw">
    update member set mPw=#{mPw} where mId=#{mId}
</update>

 

  • 결과
더보기
비밀번호 공란 시
비밀번호 오류 시
비밀번호 확인이 끝난 후
변경 비밀번호를 8글자 미만, 20글자 초과로 적을 시
비밀번호 재입력에서 변경 비밀번호와 일치하지 않을 시
비밀번호 재입력에서 변경 비밀번호와 일치 시
비밀번호 변경에 성공
비밀번호 변경 성공 후

 


 

전체 피드백

  • 아직 비밀번호가 틀렸을 경우를 작업하지 않았지만 이건 나중에 작업할 수 있도록 해두고 중요하다고 했던 게시판을 먼저 해보기로 했다.

 


728x90

'프로그래밍 > +a' 카테고리의 다른 글

slPro 7차 일지  (0) 2024.01.15
slPro 6차 일지  (1) 2024.01.11
slPro 4차 일지  (1) 2024.01.08
미니 팀 프로젝트 중간 과정 (파이썬)  (1) 2024.01.03
slPro 3차 일지  (0) 2024.01.01
728x90

팀플을 하고 나서(마지막 글은 안 올렸지만 끝났다.) 주말은 푹 쉬고 오늘부터 다시 차근차근 시작하기로 했다.


1차 일지 - [ 설정 ], [ 회원가입 ]
2차 일지 - [ 로그인 ]
3차 일지 - [ index에서 로그인 상태에 따라 display none/block 제어하고 p태그 내용 바꾸기 ], [ 로그아웃 ]

4차 일지 - [ header에 회원 아이디, 회원정보/로그아웃 링크 or 비회원 문구 추가 후 로그인 상태에 따라 display none/block 제어 ], [ 일반 회원 정보 확인 ]

 

1. header - 문구 추가 후 로그인 상태에 따라 display none/block 제어

더보기
 <script type="text/javascript">
 	$(()=>{
		console.log('loginId: ${loginId}');
		let id = '${loginId}';
		if (id != ''){
			console.log('헤더) 로그인 된 아이디 있음')
			$('#loginIdHeader').html(id+"님&ensp;");
 			if (id == 'admin'){
 				$('#checkAM').html("회원 정보 확인");
 			}
 			document.getElementById("loginIdHeaderOn").style.display = 'block';
 			document.getElementById("loginIdHeaderOff").style.display = 'none';
		}else{
			console.log('헤더) 로그인 된 아이디 없음')
		}
	})
 </script>
<div style="height: 180px; background-color: #2e2e34;">
	
	<nav class="top-right">
	
		<div>
			<nav id="loginIdHeaderOn" style="padding: 15px 10px 0px 0px; float: right; display: none;">
				<p id="loginIdHeader" style="color: #fff; float: left;">test님</p>
				&ensp;<a href="/member/info" id="checkAM">내 정보 확인</a>
				&ensp;<a href="/member/logout">로그아웃</a>&ensp;
			</nav>
			<nav id="loginIdHeaderOff" style="padding: 15px 10px 0px 0px; float: right; display: block;">
				<p id="loginIdHeader" style="color: #fff; float: left;">현재 로그인 상태가 아닙니다.</p>
			</nav>
		</div>
		
		<ul class="top-menu" style="color: #d8d3cd">
			<li><a>menu1</a></li>
			<li><a>menu2</a></li>
			<li><a>menu3</a></li>
			<li><a>menu4</a></li>
		</ul>
		
	</nav>
	
</div>
  • 정보 확인을 만들기 전에 쉬운 것부터 하기 위해 이것부터 진행했다.

 

비로그인 시
일반 회원 로그인 시
관리자 로그인 시
  • 아직 index(메인화면)에서 노란박스를 변경하지 않았는데, 어떻게 변경하면 좋을지 고민중이다.

 

 

2. 정보 확인

  • MemberController.java
더보기
@GetMapping("/member/info")
public String info(Model model, HttpSession session) {
    log.info("==> GetMapping - memberInfo 요청 <==");
    return "memberInfo";
}
  • memberInfo.jsp 화면으로 넘김

 

  • memberInfo.jsp
더보기
  • 비동기 통신으로 할 필요는 없었지만, 연습 겸 비동기 통신으로 했다.

 

  • body
<div>
    <p>현재 로그인 중인 id: ${loginId}</p>

    <div id="infoPwCh" style="display: block;">
        <input type="password" name="mPw" id="infoMPw" class="input" placeholder="비밀번호를 입력하세요" maxlength="20" autocapitalize="off">
        <div id="infoCh" style="display: none;"></div>
        <button type="submit" class="btn btn-3 btn-3e" id="infoSubBtn" style="text-align: center;">비밀번호 확인</button>
    </div>
    <div id="info">
    </div>
    <br>
    <button type="button" onclick="location.href='/'">메인화면</button>
</div>

 

  • script
<script type="text/javascript">
    let loginId = '<%=(String)session.getAttribute("loginId")%>';
    $('#infoSubBtn').on('click',function(){
        let pw = $('#infoMPw').val();
        if (pw==''){
            $('#infoCh').html('비밀번호를 입력해주세요.').css('color','red');
            document.getElementById("infoCh").style.display = 'block';
            $('#infoMPw').focus();
            return;
        }
        let chPwSend = {mPw:pw};
        console.log("chPwSend: ",chPwSend);
        $.ajax({
            method:'get',	// post로 보낼 필요 없음
            url: '/member/memberInfo',
            data: chPwSend,
        }).done(function(res){
            console.log("res: ",res);
            let mDto = res;

            if(loginId=="admin"){
                $("#info").html("아직 작업 안 함!");
                document.getElementById("infoPwCh").style.display = 'none';
            }else{
                $("#info").html("<p>이름: "+mDto.mname+"</p>");
                $("#info").append("<p>이메일: "+mDto.memail+"</p>");
                if (mDto.gName!=null){
                    $("#info").append("<p>게임 닉네임: "+mDto.gname+"</p>");
                }
                $("#info").append("<p>가입 날짜: "+mDto.joinDate+"</p>");
                document.getElementById("infoPwCh").style.display = 'none';
            }

        }).fail((err,status)=>{
            console.log("err:", err);
            console.log("status:", status);
            $("#info").html("비밀번호가 틀렸습니다.");

        })
    });
</script>

 

  • MemberAsyController.java
더보기
@GetMapping("/member/memberInfo")
public MemberDto memberInfo(@RequestParam(name = "mPw") String mPw, HttpSession session) {
    log.info("==> GetMapping - memberInfo 요청 <==");
    String mId = session.getAttribute("loginId").toString();
    if (mId.equals("admin")) {
        //return mSer.mInfoAd();
    }

    return mSer.memberInfo(mId, mPw);
}
  • 주석 처리 해둔 부분은 관리자일 경우의 코드라 대충 정해놓기만 했다.

 

  • MemberService.java
더보기
public MemberDto memberInfo(String mId, String mPw) {
    log.info("===> mSer memberInfo 요청 <===");
    BCryptPasswordEncoder pwEn = new BCryptPasswordEncoder();
    MemberDto mDto = mDao.memberInfo(mId);
    if(pwEn.matches(mPw, mDto.getMPw())) {
        return mDto;
    }
    return null;
}
  • 사실 로그인 메소드를 사용하고 그 다음에 정보를 가져갈까? 했는데 그러면 불필요하게 DB를 두 번이나 가야하기 때문에 패스했다.
  • BCryptPasswordEncoder의 matches는 무조건 앞에 변형 전 문자, 뒤에 변형 후 문자가 들어간다!

 

  • MemberDao (java, xml)
더보기
  • .java
MemberDto memberInfo(String mId);

 

  • .xml
<select id="memberInfo" parameterType="String">
    select * from member where mId=#{param1}
</select>

 아직 이해하지 못했는데, 여기서 mId를 사용하니 파라미터가 없다면서 오류가 났다. 다른 것들에서는 잘 사용했는데 왜..? 반환값이 클래스라서 그런가..? 이 부분은 더 공부를 해야할 것 같다.

 

  • 결과
더보기
처음 화면
공란으로 버튼 클릭 시

 

  • 일반 회원

 

  • 관리자 (아직 안 함)

 


 

전체 피드백

  • index의 노란박스 고민) 헤더에서 해당 페이지가 무슨 페이지인지 확인 후 그것에 따라 변경하는 것이 가능하다면 헤더에 넣은 아이템들을 index에서만 저 노란박스에 보이게 하고, 그 외에는 헤더에서만 보이게 하고 싶은데 몇번 시도했으나 실패했고, 가능할지 모르겠다.
  • 비밀번호 틀렸을 때의 경우를 작업 안 했다! 작업할 것!!

 


 

728x90

'프로그래밍 > +a' 카테고리의 다른 글

slPro 6차 일지  (1) 2024.01.11
slPro 5차 일지  (0) 2024.01.09
미니 팀 프로젝트 중간 과정 (파이썬)  (1) 2024.01.03
slPro 3차 일지  (0) 2024.01.01
미니 팀 프로젝트 사전 준비 (파이썬)  (2) 2024.01.01
728x90

 어제 회의를 통해 무엇을 할 지 정확하게 정했고, 각자 맡은 부분에서 필요한 컬럼과 과제가 컬럼 30개였으니 추가적으로 넣을 컬럼을 고른 다음에서야 데이터를 뽑기 시작해서 데이터를 뽑는 것으로 끝났다.
 각자 티어를 맡아 데이터를 받아오고, 나중에 받아온 데이터들을 cvs 파일로 공유하기로 해서 나는 내 노트북으로는 다이아, 학원에서 내 자리의 컴퓨터로는 브론즈를 받아오기로 했는데 노는 api키가 없어서 브론즈는 오늘 학원에 가서 급하게 조금 받아왔다. (그래서 다이아는 전처리 없이 10만 데이터인데 브론즈는 전처리 없이 3만 데이터였다..내일 마무리 작업 전에 브론즈는 조금 더 받아와야겠다.)

내가 맡은 부분: 
 데이터 티어: 다이아 / 브론즈
 시각 데이터: 닉네임 길이 별 승률 / 퍼블이 가장 많이 나오는 라인
(추가적으로 팀원이랑 PPT 작업도 같이 했다.)

 

1. 연습용: 다이아로만 시각화 해보기

 1-0) 시작 전

더보기
import my_utils as mu
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import font_manager, rc
import seaborn as sns
from matplotlib.gridspec import GridSpec
  • import

font_path = "C:/Windows/Fonts/gulim.ttc"
font = font_manager.FontProperties(fname=font_path).get_name()
rc('font', family=font) # 윈도우
  • 차트에 한글로 작성 시 깨지지 않게 하기 위한 설정

conn = mu.connect_mysql()
query = 'select * from lol_datas where tier="DIAMOND"'
df = pd.DataFrame(mu.sql_execute_dict(conn,query))
conn.close()
  • DB에 넣어둔 데이터를 불러와 데이터 프레임으로 저장

 

 1-1) 닉네임 길이 별 승률

더보기
tnw_df = df[['tier','summonerName','win']]
tnw_df = pd.DataFrame(tnw_df[tnw_df.summonerName.str.len()!=0])
tnw_df['sNameLen'] = tnw_df['summonerName'].str.len()
tnw_df['cnt'] = 1
tnw_df = tnw_df[['tier','summonerName','sNameLen','win','cnt']]
tnw_df['win'] = tnw_df.apply(lambda x: 1 if x.win=='True' else 0,axis=1)
가린 부분은 유저들 닉네임이다.

tnw_df2 = tnw_df[['sNameLen','win','cnt']].groupby(['sNameLen']).sum()
tnw_df2['oow'] = round((tnw_df2['win']/tnw_df2['cnt'])*100,2)
oow = 승률

list_x = [3,4,5,6,7,8,9,10,11,12,13,14,15,16]
list_y = tnw_df2['oow']
plt.bar(list_x, list_y,
         color='skyblue')
plt.xticks(list_x)
plt.yticks([0,20,50,80])

for i, v in enumerate(list_x):			# 막대 바에 값 입력
    plt.text(v, y[i], y[i],                 # 좌표 (x축 = v, y축 = y[0]..y[1], 표시 = y[0]..y[1])
             fontsize = 9, 
             color='black',
             horizontalalignment='center',  # horizontalalignment (left, center, right)
             verticalalignment='bottom')    # verticalalignment (top, center, bottom)

plt.grid(False)
  • 컬럼을 그룹바이하면서 인덱스로 만들었는데 이걸 푸는 방법을 몰랐어서 직접 적었으나 인덱스를 풀면 컬럼으로 y입력이 가능해서 노가다 없이 편하게 할 수 있다..
  • 인덱스 풀기: 데이터프레임명.reset_index()

 

 1-2) 퍼블이 가장 많이 나오는 라인

더보기
fbl = df[['gameId','championName','teamPosition','firstBloodKill']]
fbl['firstBloodKill'] = fbl.apply(lambda x: 1 if x.firstBloodKill=='True' else 0,axis=1)
가린 부분은 게임 아이디와 유저들 닉네임이다.

fblC = fbl[['teamPosition','firstBloodKill']].groupby(['teamPosition']).sum()

plt.figure()
wedgeprops={'width': 0.7, 'edgecolor': 'w', 'linewidth': 5}
plt.pie(fblC['firstBloodKill'], labels = ['지워야하는데이터','BOTTOM',' JUNGLE','MIDDLE','TOP','UTILITY'], autopct ='%1.1f%%',
       explode = (0,0,0,0,0,0), colors = ['lightblue','lightpink','#87c1e9','#84c07a','#3a8299','#5b6078'], wedgeprops=wedgeprops)
plt.legend(loc='lower right', bbox_to_anchor=(1.4,0.8))

plt.title('퍼블 라인 파이차트')
plt.grid(True)
  • 전처리를 하지 않았기 때문에 지워야하는 빈 칸의 데이터가 존재했다. (라인이 존재하지 않는 게임)

 

 

2. 시각화 (충분하게 데이터를 가져오지 못했기 때문에 다이아 외엔 데이터가 적은 수로 진행했다.)

 2-0) 시작 전

더보기
import my_utils as mu
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import font_manager, rc
import seaborn as sns
from matplotlib.gridspec import GridSpec
import numpy as np

font_path = "C:/Windows/Fonts/gulim.ttc"
font = font_manager.FontProperties(fname=font_path).get_name()
rc('font', family=font) # 윈도우

def dfc(tier):
    query = f'select * from lol_datas where tier="{tier}"'
    return pd.DataFrame(mu.sql_execute_dict(conn,query))
conn = mu.connect_mysql()
tiers = ['BRONZE','SILVER','GOLD','PLATINUM','DIAMOND']
br = dfc('BRONZE')
si = dfc('SILVER')
go = dfc('GOLD')
pl = dfc('PLATINUM')
di = dfc('DIAMOND')
conn.close()
  • 가린 부분은 게임 아이디, 게임시간, 게임버전, 유저들 닉네임이다.

 

 2-1) 닉네임 길이 별 승률

  • for문으로 돌리려고 했는데 시행착오를 거쳤음에도 방법을 찾지 못했다..
더보기
brDF = br[['tier','summonerName','win']]
brDF = pd.DataFrame(brDF[brDF.summonerName.str.len()!=0])
brDF['sNameLen'] = brDF['summonerName'].str.len()
brDF['cnt'] = 1
brDF = brDF[['tier','summonerName','sNameLen','win','cnt']]
brDF['win'] = brDF.apply(lambda x: 1 if x.win=='True' else 0,axis=1)
brDF = brDF[['sNameLen','win','cnt']].groupby(['sNameLen']).sum()
brDF['oow'] = round((brDF['win']/brDF['cnt'])*100,2)

siDF = si[['tier','summonerName','win']]
siDF = pd.DataFrame(siDF[siDF.summonerName.str.len()!=0])
siDF['sNameLen'] = siDF['summonerName'].str.len()
siDF['cnt'] = 1
siDF = siDF[['tier','summonerName','sNameLen','win','cnt']]
siDF['win'] = siDF.apply(lambda x: 1 if x.win=='True' else 0,axis=1)
siDF = siDF[['sNameLen','win','cnt']].groupby(['sNameLen']).sum()
siDF['oow'] = round((siDF['win']/siDF['cnt'])*100,2)

goDF = go[['tier','summonerName','win']]
goDF = pd.DataFrame(goDF[goDF.summonerName.str.len()!=0])
goDF['sNameLen'] = goDF['summonerName'].str.len()
goDF['cnt'] = 1
goDF = goDF[['tier','summonerName','sNameLen','win','cnt']]
goDF['win'] = goDF.apply(lambda x: 1 if x.win=='True' else 0,axis=1)
goDF = goDF[['sNameLen','win','cnt']].groupby(['sNameLen']).sum()
goDF['oow'] = round((goDF['win']/goDF['cnt'])*100,2)

plDF = pl[['tier','summonerName','win']]
plDF = pd.DataFrame(plDF[plDF.summonerName.str.len()!=0])
plDF['sNameLen'] = plDF['summonerName'].str.len()
plDF['cnt'] = 1
plDF = plDF[['tier','summonerName','sNameLen','win','cnt']]
plDF['win'] = plDF.apply(lambda x: 1 if x.win=='True' else 0,axis=1)
plDF = plDF[['sNameLen','win','cnt']].groupby(['sNameLen']).sum()
plDF['oow'] = round((plDF['win']/plDF['cnt'])*100,2)

diDF = di[['tier','summonerName','win']]
diDF = pd.DataFrame(diDF[diDF.summonerName.str.len()!=0])
diDF['sNameLen'] = diDF['summonerName'].str.len()
diDF['cnt'] = 1
diDF = diDF[['tier','summonerName','sNameLen','win','cnt']]
diDF['win'] = diDF.apply(lambda x: 1 if x.win=='True' else 0,axis=1)
diDF = diDF[['sNameLen','win','cnt']].groupby(['sNameLen']).sum()
diDF['oow'] = round((diDF['win']/diDF['cnt'])*100,2)

name = [3,4,5,6,7,8,9,10,11,12,13,14,15,16]
# 그림 사이즈, 바 굵기 조정
fig, ax = plt.subplots(figsize=(12,6))
bar_width = 0.15

# 닉네임 길이 개수
index = np.arange(14)

# 바
b1 = plt.bar(index, brDF['oow'], bar_width, alpha=0.4, color='brown', label='Bronze')
b2 = plt.bar(index + bar_width, siDF['oow'], bar_width, alpha=0.4, color='silver', label='Silver')
b3 = plt.bar(index + 2 * bar_width, goDF['oow'], bar_width, alpha=0.4, color='Gold', label='Gold')
b4 = plt.bar(index + 3 * bar_width, plDF['oow'], bar_width, alpha=0.4, color='#287a86', label='Platinum')
b5 = plt.bar(index + 4 * bar_width, diDF['oow'], bar_width, alpha=0.4, color='#316bd1', label='Diamond')

plt.xticks(np.arange(bar_width, 14 + bar_width, 1), name)
plt.ylim(0,100)

# x축, y축 이름 및 범례 설정
plt.xlabel('닉네임 길이', size = 14)
plt.ylabel('승률', size = 13)
plt.legend(labels=['Bronze','Silver','Gold','Platinum','Diamond'], loc='lower right', bbox_to_anchor=(1.15,0.75))
plt.grid(False)
  • 바에 값을 적는건 어째서인지 자꾸 에러가 나서 지웠더니 에러가 사라졌다..ㅠㅠ

 

 2-2) 퍼블이 가장 많이 나오는 라인

더보기
fbr = br[['gameId','championName','teamPosition','firstBloodKill']]
fbr['firstBloodKill'] = fbr.apply(lambda x: 1 if x.firstBloodKill=='True' else 0,axis=1)
fbr = fbr[['teamPosition','firstBloodKill']].groupby(['teamPosition']).sum()

fsi = si[['gameId','championName','teamPosition','firstBloodKill']]
fsi['firstBloodKill'] = fsi.apply(lambda x: 1 if x.firstBloodKill=='True' else 0,axis=1)
fsi = fsi[['teamPosition','firstBloodKill']].groupby(['teamPosition']).sum()

fgo = go[['gameId','championName','teamPosition','firstBloodKill']]
fgo['firstBloodKill'] = fgo.apply(lambda x: 1 if x.firstBloodKill=='True' else 0,axis=1)
fgo = fgo[['teamPosition','firstBloodKill']].groupby(['teamPosition']).sum()

fpl = pl[['gameId','championName','teamPosition','firstBloodKill']]
fpl['firstBloodKill'] = fpl.apply(lambda x: 1 if x.firstBloodKill=='True' else 0,axis=1)
fpl = fpl[['teamPosition','firstBloodKill']].groupby(['teamPosition']).sum()

fdi = di[['gameId','championName','teamPosition','firstBloodKill']]
fdi['firstBloodKill'] = fdi.apply(lambda x: 1 if x.firstBloodKill=='True' else 0,axis=1)
fdi = fdi[['teamPosition','firstBloodKill']].groupby(['teamPosition']).sum()

# 그리드 스펙 설정
gs = GridSpec(3, 3)
# 이미지 사이즈 설정
fig = plt.figure(figsize=(15, 15))

# 크기와 위치가 다른 서브플롯 생성
ax1 = fig.add_subplot(gs[1, 0])
ax2 = fig.add_subplot(gs[1, 1])
ax3 = fig.add_subplot(gs[2, 0])
ax4 = fig.add_subplot(gs[2, 1])
ax5 = fig.add_subplot(gs[2, 2])

wedgeprops={'width': 0.7, 'edgecolor': 'w', 'linewidth': 5} # 파이차트를 부채꼴로 만듦

ax1.pie(fbr['firstBloodKill'], autopct ='%1.1f%%',
       explode = (0,0,0,0,0,0), colors = ['lightblue','lightpink','#87c1e9','#84c07a','#3a8299','#5b6078'], wedgeprops=wedgeprops)
ax2.pie(fsi['firstBloodKill'], autopct ='%1.1f%%',
       explode = (0,0,0,0,0,0), colors = ['lightblue','lightpink','#87c1e9','#84c07a','#3a8299','#5b6078'], wedgeprops=wedgeprops)
ax3.pie(fgo['firstBloodKill'], autopct ='%1.1f%%',
       explode = (0,0,0,0,0,0), colors = ['lightblue','lightpink','#87c1e9','#84c07a','#3a8299','#5b6078'], wedgeprops=wedgeprops)
ax4.pie(fpl['firstBloodKill'], autopct ='%1.1f%%',
       explode = (0,0,0,0,0,0), colors = ['lightblue','lightpink','#87c1e9','#84c07a','#3a8299','#5b6078'], wedgeprops=wedgeprops)
ax5.pie(fdi['firstBloodKill'], autopct ='%1.1f%%',
       explode = (0,0,0,0,0,0), colors = ['lightblue','lightpink','#87c1e9','#84c07a','#3a8299','#5b6078'], wedgeprops=wedgeprops)


ax1.set_title('브론즈')
ax2.set_title('실버')
ax3.set_title('골드')
ax4.set_title('플래티넘')
ax5.set_title('다이아')


plt.legend(labels=['지워야함','BOTTOM','JUNGLE','MIDDLE','TOP','UTILITY'], loc='lower right', bbox_to_anchor=(0.7,1.6))
plt.show()
  • 전처리를 하지 않았기 때문에 지워야하는 빈 칸의 데이터가 존재했다. (라인이 존재하지 않는 게임)

 


 

참고한 글 링크 목록

 


 

728x90

'프로그래밍 > +a' 카테고리의 다른 글

slPro 5차 일지  (0) 2024.01.09
slPro 4차 일지  (1) 2024.01.08
slPro 3차 일지  (0) 2024.01.01
미니 팀 프로젝트 사전 준비 (파이썬)  (2) 2024.01.01
slPro 2차 일지  (0) 2023.12.28
1 2 3 4 5