🎈 블로그 - https://gomawoomi.tistory.com/

💾 Github - https://github.com/jinseong205/springBoot-blog

📙개발과정 -  https://gomawoomi.tistory.com/category/Project/SpringBoot-Blog

 


 

설명 

개인 관련 자료 정리 및 토이프로젝트를 구현하는 사이트입니다.

 


 

개발환경

Oracle Cloud Service - compute instance (Ubuntu - 22.04)
Oracle Cloud Service - Autonomous Data Warehouse (Oracle 19c)

 

언어 및 프레임 워크

Java / JSP
Spring Boot
Oracle 

 

사용기술

#JPA    #Spring Security    #oAuth (kakao)    #REST API    #Ajax   

#Canvas    #Riot API    #CK Editor    #BootStrap    #WebSocket 

  


 

메인화면

 

기본 메인 화면으로 블로그내의 [프로필], [포트폴리오], [토이 프로젝트] 로의 연결과 외부 [개인 블로그], [Github]으로 연결합니다.

또한 개인 GitHub의 contribution을 연결 해 두었습니다.

 


 

회원가입 / 로그인 

 

간단한 회원정보를 통해 회원가입을 진행하고 있습니다.

회원가입시 유저의 패스워드는 Spring Security 에서 지원하는 BCryptPasswordEncoder를 사용하여 Hashing하여 저장됩니다.

로그인은 Spring Security 에서 AuthenticationManager 를 활용하여 로그인을 진행하였습니다.

카카오톡을 이용한 oAuth경우 로그인시 자동으로 회원가입을 진행하여 로그인을 진행하고 있습니다.

 

 


 

[자료정리] 글 목록 / 글 자세히 보기

 

글목록은 카테고리별로 5개씩 페이징 처리하여 글의 제목과 작성자, 작성시간을  확인할 수 있습니다.

자료는 프로필 / 포트폴리오 / 자유게시판 3개의 항목으로 분류되었습니다.

 

글 자세히보기는 해당글의 내용을 확인할 수 있으며 자신이 등록한 글에 대해 수정/삭제가 지원됩니다.

댓글의 경우 등록 및 자신이 등록한 댓글에 대해 삭제가 지원됩니다.

관리자 권한은 모든 글/댓글에 대해서 삭제가 가능합니다.

 

 

 


 

[자료정리] 글작성 / 수정

 

글 작성/수정 은 자유게시판에 대해서는 모든 회원이 글을 작성할수 있습니다.

관리자 계정은 모든 카레고리의 글을 작성 할 수 있습니다.

 

에디터는 CkEditor를 사용하였으며 image Upload 기능을 구현하였습니다.

 

 

 


 

[토이 프로젝트] Canvas 웹게임

 

HTML Canvas를 활용하여 간단한 웹게임을 구현하였습니다.

고양이 캐릭터를 마우스/키보드를 사용하여 재생되는 화살을 피하는 게임입니다.

화살이 고양이 캐릭터에 충돌처리 되면 게임이 종료됩니다.

 

『유니티5 교과서』 서적에서 제공하는 리소스를 사용하였습니다.

 


 

[토이 프로젝트] RiotApi

 

Riot Api 를 활용하여 LeagueOfLegend 의 계정 정보와 최근 5경기의 전적을 조회하는 기능을 구현 하였습니다.

개발 라이센스로는 시간당 Request의 갯수가 제한되어 일부정보만을 제공하고 있습니다.

또한 api-key가 24시간이 지나면 만료되기 때문에 api-key가 만료 되면 dummy 데이터의 정보를 조회하고 있습니다.

 

 


 

[토이 프로젝트] WebSocket 채팅

 

WebSocket 를 활용하여 간단한 멀티 채팅을 구현하였습니다.

로그인 사용자의 경우에는 계정id로 비로그인 사용자의 경우 랜덤아이디를 생성하여 채팅방에 참여할 수 있습니다.

 

 


 

[토이 프로젝트]

 

토이프로젝트는 관심있는 스킬을 간단한게 구현하여 블로그에 업로드 중입니다.

지속적인 업데이트가 진행 되고 있습니다.

 

Canvas 웹 게임 - https://gomawoomi.tistory.com/54

Riot API - https://gomawoomi.tistory.com/55

WebSocket 채팅 - https://gomawoomi.tistory.com/59

 

 

 



'Project > Blog' 카테고리의 다른 글

Blog - Web Chatting  (2692) 2022.07.20
Blog - Riot API  (2684) 2022.07.05
Blog -Canvas를 이용한 WebGame  (1731) 2022.07.05
Blog - Oracle Cloud 에 Spring Boot War 배포하기  (482) 2022.06.29
Blog - 10. oAuth (카카오 로그인)  (490) 2022.06.23

 

[결과물]

web socket 을 이용한 멀티 채팅을 구현한다.

[pom.xml]

<!-- ... -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-websocket</artifactId>
		</dependency>
<!-- ... -->

 

[SocketHandler.java]

package com.jinseong.blog.handler;

@Component
public class SocketHandler extends TextWebSocketHandler {

	HashMap<String, WebSocketSession> sessionMap = new HashMap<>();
	
	//socket Connected
	@Override
	public void afterConnectionEstablished(WebSocketSession session) throws Exception {
		super.afterConnectionEstablished(session);
		sessionMap.put(session.getId(), session);
	}

	//socket Closed
	@Override
	public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
		sessionMap.remove(session.getId());
		super.afterConnectionClosed(session, status);
	}
	
	//massageSend
	@Override
	protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
		String msg = message.getPayload();
		for(String key : sessionMap.keySet()) {
			WebSocketSession wss = sessionMap.get(key);
			try {
				wss.sendMessage(new TextMessage(msg));
			}catch(Exception e){
				e.printStackTrace();
			}
		}
		
		super.handleTextMessage(session, message);
	}
}

[WebSocketConfig.java]

package com.jinseong.blog.config;

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
	@Autowired
	SocketHandler socketHandler;

	@Override
	public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
		registry.addHandler(socketHandler, "/toy/chatting");
	}
	
}

 

[ToyController]

package com.jinseong.blog.controller.common;

@Controller
public class ToyController {

    /* ... */

	@Autowired
	private WebSocketChattingService webSocketChattingService;
	
    /* ... */
    
	@GetMapping("toy/webSocketChatting")
	public String webSocketChatting(Model model) {
		String chatName = webSocketChattingService.getChatName();
		model.addAttribute("chatName",chatName);
		return "toy/webSocketChatting/main";
	}

}

 

[webSocketChatting.js]

let index = {
	init: function() {
		wsOpen();
	}
}

var ws;

function wsOpen() {
	ws = new WebSocket("ws://" + location.host + "/toy/chatting")
	wsEvt();
}

function wsEvt() {
	ws.onopen = function(data) {
		//socket init 초기화
	}

	ws.onmessage = function(data) {
		var msg = data.data;
		if (msg != null && msg.trim() != '') {
			$("#chattingSpace").append("<p>" + msg + "</p>");
		}
	}

	document.addEventListener("keypress", function(e) {
		if (e.keyCode == 13) {	//enter press
			sendChat();
		}
	})

}

function sendChat() {
	var chatName = $("#chatName").val();
	var msg = $("#chatMsg").val();
	ws.send("[" + chatName + "] " + msg);
	$('#chatMsg').val("");
	$("#chattingSpace").scrollTop($("#chattingSpace")[0].scrollHeight);
	
}


index.init();

 

[main.jsp]

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%@include file="../../layout/header.jsp"%>


<body class="">
	<div class="d-flex" id="wrapper">
		<%@include file="../../layout/toySideBar.jsp"%>
		<div id="page-content-wrapper">
			<%@include file="../../layout/nav.jsp"%>

			<div id="content" class="container-fluid ">
				<br />
				<div class="clearfix container-fluid">
					<div class="float-left">
						<button class="btn btn-primary" id="sidebarToggle"><></button>

					</div>
				</div>
				<br />
				<div class="ml-5 mr-5 mb-3">
					<h2>WebSocket 채팅</h2>
					웹소켓을 이용하여 간단한 채팅방을 구현하였습니다. <br />메세지 내용은 별도로 저장되지 않습니다. <br />개발 일지 - <a href="https://gomawoomi.tistory.com/56">https://gomawoomi.tistory.com/56</a>
				</div>
				<div class="ml-5 mr-5 mb-5">
					<div id="chattingSpace" class="chattingSpace p-3">
						${chatName}님 환영합니다.
						<hr />
					</div>

					<input id="chatName" name="chatName" class="form-control form-control-lg form-control-borderless" type="hidden" value="${chatName}">

					<div id="chatMsgDiv" class="card-body row no-gutters align-items-center">
						<div class="col">
							<input id="chatMsg" name="chatMsg" class="form-control form-control-lg form-control-borderless" type="search" placeholder="대화를 입력하세요." />
						</div>
						<div class="col-auto">
							<button id="sendChatBtn" class="btn btn-lg btn-primary" onclick="sendChat()">&nbsp전송&nbsp</button>
						</div>
					</div>

				</div>
			</div>
			<%@include file="../../layout/footer.jsp"%>
		</div>
		<script src="/js/toy/webSocketChatting.js"></script>
	</div>

</body>
</html>

'Project > Blog' 카테고리의 다른 글

Blog - 완성  (10251) 2023.03.03
Blog - Riot API  (2684) 2022.07.05
Blog -Canvas를 이용한 WebGame  (1731) 2022.07.05
Blog - Oracle Cloud 에 Spring Boot War 배포하기  (482) 2022.06.29
Blog - 10. oAuth (카카오 로그인)  (490) 2022.06.23

[결과물]

[api key 발급]

https://developer.riotgames.com/

 

Riot Developer Portal

About the Riot Games API With this site we hope to provide the League of Legends developer community with access to game data in a secure and reliable way. This is just part of our ongoing effort to respond to players' and developers' requests for data and

developer.riotgames.com

 

 

 

[sommonerName으로 sommoner 찾기]

소환사명으로 sommoner를 찾으면 summonerId, puuid 뿐만 아니라 계정레벨 등 계정 정보를 찾을 수 있다.

/lol/summoner/v4/summoners/by-name/{summonerName}

 - Request

- Response

[sommonerId으로 entries 찾기]

summonerId를 통해 현재 티어, 랭크, 리그 포인트 등 계정에 관한 정보를 찾을 수 있다.

/lol/league/v4/entries/by-summoner/{encryptedSummonerId}

 

[puuid로 matches 찾기]

puuid를 통해 해당계정이 플레이한 matchId를 찾을 수 있다.

/lol/match/v5/matches/by-puuid/{puuid}/ids

 

[matchId 로 matches 찾기]

matchId를 통해 해당 게임의 정보를 얻을 수 있다. 승/패여부, 모든 참가자에 대한 게임정보등이 포함되어잇다.

/lol/match/v5/matches/{matchId}

 

[RiotApiService]

@Service
public class RiotApiService {

	@Value("${riot.api.key}")
	private String riotKey;

	@Value("${riot.api.summoner-url}")
	private String summonerUrl;

	@Value("${riot.api.league-etnry-url}")
	private String leagueEntryUrl;

	@Value("${riot.api.match-by-puuid-url}")
	private String matchByPuuuidUrl;

	@Value("${riot.api.match-by-MatchId-url}")
	private String matchByMatchIdurl;

	public RiotInfo riotInfo(String username) throws Exception {

		RiotInfo riotInfo = new RiotInfo();
		WebClient client = WebClient.create();

		/* Summoner */
		String summonerReqUrl = summonerUrl.replace("{text}", username);

		Mono<String> monoString = client.get().uri(summonerReqUrl).headers(headers -> {
			headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
			headers.add("User-Agent",
					"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.164 Safari/537.36");
			headers.add("Accept-Charset", "application/x-www-form-urlencoded; charset=UTF-8");
			headers.add("Origin", "https://developer.riotgames.com");
			headers.add("X-Riot-Token", riotKey);
		}).retrieve().bodyToMono(String.class);

		JSONObject sommonerJson = new JSONObject(monoString.block());

		riotInfo.setId(sommonerJson.get("id").toString());
		riotInfo.setPuuid(sommonerJson.get("puuid").toString());
		riotInfo.setName(sommonerJson.get("name").toString());
		riotInfo.setSummonerLevel(sommonerJson.get("summonerLevel").toString());

		/* League Entry */
		String leagueEntryReqUrl = leagueEntryUrl.replace("{text}", riotInfo.getId());
		monoString = client.get().uri(leagueEntryReqUrl).headers(headers -> {
			headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
			headers.add("User-Agent",
					"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.164 Safari/537.36");
			headers.add("Accept-Charset", "application/x-www-form-urlencoded; charset=UTF-8");
			headers.add("Origin", "https://developer.riotgames.com");
			headers.add("X-Riot-Token", riotKey);
		}).retrieve().bodyToMono(String.class);

		boolean isEntry = new JSONArray(monoString.block()).length() > 0;

		if (isEntry) {
			JSONObject leagueEntryJson = new JSONArray(monoString.block()).getJSONObject(0);
			riotInfo.setTier(leagueEntryJson.get("tier").toString());
			riotInfo.setRank(leagueEntryJson.get("rank").toString());
			riotInfo.setLeaguePoints(leagueEntryJson.get("leaguePoints").toString());
			riotInfo.setWins(leagueEntryJson.get("wins").toString());
			riotInfo.setLosses(leagueEntryJson.get("losses").toString());
		}

		/* match By puuuid */
		String matchByPuuuidReqUrl = matchByPuuuidUrl.replace("{text}", riotInfo.getPuuid());
		monoString = client.get().uri(matchByPuuuidReqUrl).headers(headers -> {
			headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
			headers.add("User-Agent",
					"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.164 Safari/537.36");
			headers.add("Accept-Charset", "application/x-www-form-urlencoded; charset=UTF-8");
			headers.add("Origin", "https://developer.riotgames.com");
			headers.add("X-Riot-Token", riotKey);
		}).retrieve().bodyToMono(String.class);

		String[] matchIds = null;
		if (monoString.block() != null && !monoString.block().replaceAll(" ", "").equals("[]")) {
			matchIds = monoString.block().split(",");
		}
		
        /* match By matchId */
		String matchByMatchIdReqUrl;
		String matchId;

		List<RiotMatch> matchs = new ArrayList<RiotMatch>();

		RiotMatch riotMatch = null;
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm");

		for (int i = 0; i < matchIds.length; i++) {
			matchId = matchIds[i].replace("[", "").replace("]", "").replaceAll("\"", "").trim();

			matchByMatchIdReqUrl = matchByMatchIdurl.replace("{text}", matchId);

			monoString = client.get().uri(matchByMatchIdReqUrl).headers(headers -> {
				headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
				headers.add("User-Agent",
						"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.164 Safari/537.36");
				headers.add("Accept-Charset", "application/x-www-form-urlencoded; charset=UTF-8");
				headers.add("Origin", "https://developer.riotgames.com");
				headers.add("X-Riot-Token", riotKey);
			}).retrieve().bodyToMono(String.class);

			JSONObject matchInfoJson = new JSONObject(monoString.block()).getJSONObject("info");

			riotMatch = new RiotMatch();

			Timestamp stratTimeStamp = null;
			Timestamp endTimeStamp = null;

			try {
				stratTimeStamp = new Timestamp(Long.parseLong(matchInfoJson.get("gameStartTimestamp").toString()));
				endTimeStamp = new Timestamp(Long.parseLong(matchInfoJson.get("gameEndTimestamp").toString()));
				riotMatch.setGameStartTimestamp(sdf.format(stratTimeStamp));
				riotMatch.setGameEndTimestamp(sdf.format(endTimeStamp));
			} catch (Exception e) {
			}

			riotMatch.setGameMode(matchInfoJson.getString("gameMode"));

			JSONArray participantsJsonArr = matchInfoJson.getJSONArray("participants");

			JSONObject participantsJson;
			for (int j = 0; j < participantsJsonArr.length(); j++) {
				participantsJson = participantsJsonArr.getJSONObject(j);
				if (participantsJson.get("summonerName").equals(username)) {
					riotMatch.setAssists(participantsJson.get("assists").toString());
					riotMatch.setKills(participantsJson.get("kills").toString());
					riotMatch.setDeaths(participantsJson.get("deaths").toString());
					riotMatch.setChampionName(participantsJson.get("championName").toString());

					if (participantsJson.getBoolean("win")) {
						riotMatch.setWin("승리");
					} else {
						riotMatch.setWin("패배");
					}

				}
			}
			matchs.add(riotMatch);
		}

		try {
			Collections.sort(matchs);
		} catch (Exception e) {
		}
		riotInfo.setMatchs(matchs);
		return riotInfo;
	}
}

 

'Project > Blog' 카테고리의 다른 글

Blog - 완성  (10251) 2023.03.03
Blog - Web Chatting  (2692) 2022.07.20
Blog -Canvas를 이용한 WebGame  (1731) 2022.07.05
Blog - Oracle Cloud 에 Spring Boot War 배포하기  (482) 2022.06.29
Blog - 10. oAuth (카카오 로그인)  (490) 2022.06.23

[결과물]

[main.jsp]

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%@include file="../../layout/header.jsp"%>


<body class="">
	<div class="d-flex" id="wrapper">
		<%@include file="../../layout/toySideBar.jsp"%>
		<div id="page-content-wrapper">
			<%@include file="../../layout/nav.jsp"%>

			<div id="content" class="container-fluid ">
				<br /> 
				<div class="clearfix container-fluid">
					<div class="float-left">
						<button class="btn btn-primary" id="sidebarToggle"><></button>
					</div>
				</div>
				<br/>
				<canvas id="myCanvas" class="webGame" width="600" height="450"></canvas>
				<br>
				<div class="align-items-center">
					<p class="d-flex justify-content-center">Resource - 『유니티5 교과서』</p>
				</div>
			</div>
			<%@include file="../../layout/footer.jsp"%>
		</div>
		<script src="/js/toy/webGame.js"></script>
	</div>

</body>
</html>

 

[webGame.css]

.webGame {
	background: url('/image/webGame/background.png') center repeat;
	background-size: contain;
	display: block;
	margin: 0 auto;
	
}

 

[webGame.js]

var doAnim = true;

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");

var playerW = canvas.width / 8;                                         //플레이어 가로
var playerH = canvas.height / 6;                                       //플레이어 세로
var playerX = (canvas.width - playerW) / 2;                              //플레이어 x 좌표
var playerY = canvas.height - playerH - (canvas.height / 24);              //플레이어 y 좌표

var arrowW = canvas.width / 32;
var arrowH = canvas.height / 24;
var arrowX = Math.floor(Math.random() * 750) + 25;
var arrowY = 0;
var arrowdy = 8;

var rightPressed = false;
var leftPressed = false;


var myPlayer = new Image();
myPlayer.src = "/image/webGame/player.png";

var myArrow = new Image();
myArrow.src = "/image/webGame/arrow.png";

function drawplayer() {
	ctx.beginPath();
	//player init locate, size
	ctx.drawImage(myPlayer, playerX, playerY, playerW, playerH);
	var cW = ctx.canvas.width, cH = ctx.canvas.height;
	ctx.closePath();
}

function drawarrow() {
	ctx.beginPath();
	//player init locate, size
	//init x init y xsize ysize 
	ctx.drawImage(myArrow, arrowX, arrowY, arrowW, arrowH);

	var cW = ctx.canvas.width, cH = ctx.canvas.height;
	ctx.closePath();
}

function arrowReload() {
	if (arrowY > 600) {
		arrowX = Math.floor(Math.random() * 750) + 25;
		arrowY = 0;
	}
}

//충돌판정
function collisionDectection() {
	if (playerX - (playerW / 8) < arrowX - (arrowW / 2) 
	&& playerX + (playerW / 2) > arrowX - (arrowW / 2) 
	&& playerY - (playerH / 2) < arrowY - 50 + (arrowH / 2) 
	&& playerY + (playerH / 2) > arrowY - 50 + (arrowH / 2)) {
		
		var returnValue = confirm('Game over \nRetry?');

		if(returnValue){
			arrowY = 0;
			arrowdy = 8;
		}else{
			doAnim = false;
		}
	}
}

document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);
document.addEventListener("mousemove", mouseMoveHandler, false);

function keyDownHandler(e) {
	if (e.keyCode == 39) {
		rightPressed = true;
	}
	else if (e.keyCode == 37) {
		leftPressed = true;
	}
}
function keyUpHandler(e) {
	if (e.keyCode == 39) {
		rightPressed = false;
	}
	else if (e.keyCode == 37) {
		leftPressed = false;
	}
}

function mouseMoveHandler(e) {
	var relativeX = e.clientX - canvas.offsetLeft;
	if (relativeX > 0 && relativeX < canvas.width) {
		playerX = relativeX - playerW / 2;
	}
}

function draw(){

	ctx.clearRect(0, 0, canvas.width, canvas.height);
	drawplayer();
	drawarrow();
	arrowReload();
	collisionDectection()

	if(!doAnim){
		return;
	}

	if (rightPressed && playerX < canvas.width - playerW) {
		playerX += 7;
	}
	else if (leftPressed && playerX > 0) {
		playerX -= 7;
	}
	arrowY += arrowdy;
	requestAnimationFrame(draw);
}


draw();

 

 

 

리소스 - 유니티 5 교과서

'Project > Blog' 카테고리의 다른 글

Blog - Web Chatting  (2692) 2022.07.20
Blog - Riot API  (2684) 2022.07.05
Blog - Oracle Cloud 에 Spring Boot War 배포하기  (482) 2022.06.29
Blog - 10. oAuth (카카오 로그인)  (490) 2022.06.23
Blog - 9. Board CURD  (484) 2022.06.18

+ Recent posts