프리코스 1주차 리뷰
커밋은 중요하구나!
드디어 우아한테크코스 1주차가 종료되고 2주차가 시작되는 11월 첫째 주가 다가왔다.
우아한테크코스 1주차는 여러 코딩테스트 사이트에서 볼 수 있는 문제들이 나왔었다.
나는 문제들을 한번 싹 훑어보고 문제의 난이도가 엄청높아 문제의 수준으로 사람들을 판별하겠다는 의도가 느껴지기 보다는 한문제 한문제 난이도는 높이되 문제를 푸는것이 주된 목적이 아닌 git의 사용법 이해를 주된 목적으로 보고 큰 부담감없이 문제를 풀었다.
물론 이후에 코드가 너무 solution에 뭉쳐있거나 예외 처리가 하나도 되어있지 않은거 같아 리팩터링을 거치며 조금 더 효율적인 코드를 작성하고자 했으나 알고리즘 뉴비인 나는 알고리즘은 거의 대부분 사용하지 못했다..
여튼 나는 문제를 해결하는 능력도 중요하다고 생각했지만 Commit을 기능 단위 즉, 의미 단위로 커밋을 하며 Commit의 옳바른 사용과 문제의 요구사항을 분석하고 절차적으로 진행하는 것(약간 애자일을 모방한 느낌의)이 주된 목표라고 생각하고 실행했다.
참고로 이때 사용한 Commit Template은 크게 5가지로 아래와 같다.
Template 종류 | 의미와 상황 |
feat | 새로운 기능을 개발하여 처음으로 커밋을 올릴 때 사용한다 |
fix | 기존의 코드에서 변수명이나 메소드의 이름과 같은 작은 범위의 수정 |
docs | 문서작업을 하였을 때 사용 |
style | 코드에 영향을 끼치지 않는 커밋 ex) annotation |
refactor | 기존의 기능을 메서드화 시키거나 구조를 재배치하는 큰 범위의 수정 |
이를 바탕으로 커밋을 하고 내가 올린 커밋을 보며 스스로를 복기할 수 있었다. 이 또한 우테코의 순기능이라고 생각한다.
보통의 개발자들은 기능을 개발하거나 예외처리를 하고나면 대부분 코드를 다시 들여다 보지않는다. 왜냐하면 그야 정상적으로 작동하니까!(이는 개발자의 축복)
그러나 우테코를 진행하며 스스로에게 계속 피드백을 던지게 되었고 이를 처음 경험해보는 것이었기 때문에 스스로를 돌아보고 과거로 부터 미래의 발전을 본다는 것에 큰 의미를 가지게 되었다.
1주차의 핵심은 자기자신으로부터 성장이 가장 큰 주제이자 수확인거같다.
그리고 직접 우아한테크코스에서 전달한 1주차 공통피드백이 있다.
그 중 주관적으로 중점이라고 생각된 몇개만 기록해보자
1. 요구사항을 정확히 준수한다.
=> 기능 요구 사항과 프로그래밍 요구 사항, 과제 진행 요구 사항을 명확히 준수한다.
2. 커밋 메시지를 의미 있게 작성한다.
=> 기능 요구 사항과 프로그래밍 요구 사항, 과제 진행 요구 사항을 명확히 준수한다.
3. Git을 통해 관리할 자원을 고려한다.
=> Intellij의 .idea폴더나 Eclipse의 .metadata폴더같이 큰 의미를 가지지 않는 파일들은 git에 굳이 같이 올릴 필요성이 없다. Git을 통해 관리할 필요가 있는 파일만 Git 저장소로 관리한다.
4. 이름을 통해 의도를 드러낸다.
=> 다른 개발자와 소통을 위해 가장 중요한 활동 중 하나가 이름짓기이다. 변수명, 메서드 이름,클래스 이름을 의도가 잘들어나게 짓는게 중요하다. 이름을 통해 의도를 나타내며 커뮤니케이션의 주요용도로 사용한다.
5. 축약하지 않는다.
=> 이름을 통해 의도를 드러낼 수 있다면 이름이 길어져도 괜찮다. 메서드나 변수 의미를 제대로 전달할 수 있다면 굳이 축약하지않고 의미 그대로 적어 그 뜻을 나타나게 해야한다.
6. 공백 라인을 의미 있게 사용한다.
=> 공백 라인을 의미 있게 사용하는 것이 좋으며 문맥을 분리하는 용도로 사용하며 과도한 공백을 사용하지는 않는다.
역시 1주차에서 가장 중요한 부분은 기능 목록을 먼저 산출(요구사항을 먼저분석)하고 그에 따른 기능을 개발하고나면 기능별로 의미가 잘 전달될 수 있게 커밋을 하는 것이 제일 중요한것 같다. 이러한 점은 앞으로 남은 2,3,4주차에서도 잘 지켜야할 부분이라고 생각한다.
순수 자바로 처음..
나는 컴퓨터 소프트웨어 학과에 재학중이지만 자바에 대한 깊은 공부나 심화적인 학습을 하기보단 스프링을 먼저 접하고 후에 자바의 매력에 빠져 자바를 공부한 케이스였다.
그렇기 때문에 순수한 자바언어로 이러한 과제를 진행해본것이 처음이었기 때문에 상당히 난잡하게 코드를 진행하게 되었지만 역시 자바는 위대한 언어이다.(요새는 코틀린이 더..)
확실히 자바는 메서드 명이 직관적이라 기능을 구현하기 위해 무슨 메서드가 효율적인지 개발자입장에서 선택하여 사용할 수 있다는 점이 자바란 언어의 강점이 아닐까 싶다.
또한 자바는 명성에 비해 상당히 뉴비 불친화적인 언어일 것 같지만 생각보다 인터페이스를 보며 처음쓰는 구조체나 메서드라도 어렵지 않게 사용할 수 있다.
아쉬운 점
이번 1주차를 진행하며 나 자신에게 제일 아쉬웠던 부분이 알고리즘 기초가 탄탄하지 못하다는 점이었다. 예를 들어 마지막 7번문제를 해결할 때도 불필요한 loop를 통해 user의 친구를 따로 뽑고 그 이후 다시 loop를 돌린다는 점에서 이미 시간효율성과 메모리적으로도 손해가 너무 심하다고 생각했다. BFS알고리즘을 내가 정확히 이해하고 사용할 수 있었다면 좀더 객체지향적이며 효율적인 코드가 됐을 것이라는 생각이 아직까지도 조금 후회스럽지만 앞으로 나아가야겠단 의지를 더 불태워주는 촉진제가 된것 같다.
우아한테크코스를 통해 느낀것
사실 우아한테크코스를 지원할 때 부터 했던 생각이지만 나는 기초가 탄탄하지도 혁신적인 코드를 작성할 능력이 있지도 않다고 생각해 '약 1700명 가량에서 100명을 선출하는데 과연 내가 남을 수 있을까?' 라는 고민을 했었다.
하지만 이는 곧 잘못된 생각이라고 느꼈다. 우아한테크코스는 물론 나같이 발전에 방향성이 간절한 사람들에게는 절실한기회이다. 하지만 최종합격하지 못한다고 해도 난 이미 1주차를 진행하며 얻은게 있듯이 너무 목메거나 조급해하지 않아도 된다고 생각했다.
그리고 우아한테크코스는 완벽에 가까운 사람을 찾는 대회같은 것이 아닌 함께 성장을 도모하고 '같이'를 추구하는 기회인 만큼 나 또한 모르거나 부족한 부분은 당연히 배우면서도 내가 아는 것 또한 자기 피드백이나 남으로 부터 배우는 소중한 4주의 기회라고 생각하며 임할 생각이다.
7번 문제 리뷰
역시나 마지막 문제라 그런지 상당히 어려웠던 문제중 하나이다. 문제를 하나하나 뜯어보며 나의 풀이법을 살펴보자.
문제
레벨 2의 팀 프로젝트 미션으로 SNS(Social Networking Service)를 만들고자 하는 팀이 있다. 팀에 속한 크루 중 평소 알고리즘에 관심이 많은 미스터코는 친구 추천 알고리즘을 구현하고자 아래와 같은 규칙을 세웠다.
- 사용자와 함께 아는 친구의 수 = 10점
- 사용자의 타임 라인에 방문한 횟수 = 1점
사용자 아이디 user와 친구 관계 정보 friends, 사용자 타임 라인 방문 기록 visitors가 매개변수로 주어질 때, 미스터코의 친구 추천 규칙에 따라 점수가 가장 높은 순으로 정렬하여 최대 5명을 return 하도록 solution 메서드를 완성하라. 이때 추천 점수가 0점인 경우 추천하지 않으며, 추천 점수가 같은 경우는 이름순으로 정렬한다.
제한사항
- user는 길이가 1 이상 30 이하인 문자열이다.
- friends는 길이가 1 이상 10,000 이하인 리스트/배열이다.
- friends의 각 원소는 길이가 2인 리스트/배열로 [아이디 A, 아이디 B] 순으로 들어있다.
- A와 B는 친구라는 의미이다.
- 아이디는 길이가 1 이상 30 이하인 문자열이다.
- visitors는 길이가 0 이상 10,000 이하인 리스트/배열이다.
- 사용자 아이디는 알파벳 소문자로만 이루어져 있다.
- 동일한 친구 관계가 중복해서 주어지지 않는다.
- 추천할 친구가 없는 경우는 주어지지 않는다.
입출력 예제
user friends visitors result
"mrko" | [ ["donut", "andole"], ["donut", "jun"], ["donut", "mrko"], ["shakevan", "andole"], ["shakevan", "jun"], ["shakevan", "mrko"] ] | ["bedi", "bedi", "donut", "bedi", "shakevan"] | ["andole", "jun", "bedi"] |
내가 분석한 문제의 요구 사항
- 예외 처리 사항
- user는 길이가 1 이상 30 이하인 문자열이다.
- friends는 길이가 1 이상 10,000 이하인 리스트/배열이다.
- friends의 각 원소는 길이가 2인 리스트/배열로 [아이디 A, 아이디 B] 순으로 들어있다.
- A와 B는 친구라는 의미이다.
- 아이디는 길이가 1 이상 30 이하인 문자열이다.
- visitors는 길이가 0 이상 10,000 이하인 리스트/배열이다.
- 사용자 아이디는 알파벳 소문자로만 이루어져 있다.
- 기능 목록
- 추천 친구들의 점수 구하기
- 점수가 제일높은 5명 산출하기
- 추천 점수 0점은 추천하지 않고 오름차순으로 정렬
- 기존 친구는 추천목록에 뜨지않음
- 방문자의 점수 구하기
문제 구현
예외 처리 부분
private static <T>boolean validation(String err_type,T err_param){
if (err_type.equals("user_length")&&err_param instanceof Integer){
int user_length = (int) err_param;
return 0< user_length&&user_length<31;
}
else if (err_type.equals("friends_size")&&err_param instanceof Integer){
int friends_size = (int) err_param;
return 0<friends_size&&friends_size<10001;
}
else if (err_type.equals("visitors_size")&&err_param instanceof Integer){
int visitors_size = (int) err_param;
return 0<visitors_size&&visitors_size<10001;
}
else if(err_type.equals("friends_e_length")&&err_param instanceof Integer){
int elements_length = (int) err_param;
return elements_length==2;
}
else if (err_type.equals("user_valid")&&err_param instanceof String){
String user_id_regex = (String) err_param;
if (!user_id_regex.matches("^[a-z]*$")) return false;
return 0<user_id_regex.length()&&user_id_regex.length()<31;
}
return false;
}
예외처리는 private 타입의 validation이라는 메서드로 네이밍 하여서 인자값으로 에러의 타입(문자형),제네릭으로 예외처리를 할 값을 받아준다.
- user는 길이가 1 이상 30 이하인 문자열이다.
- friends는 길이가 1 이상 10,000 이하인 리스트/배열이다.
- friends의 각 원소는 길이가 2인 리스트/배열로 [아이디 A, 아이디 B] 순으로 들어있다.
- A와 B는 친구라는 의미이다.
- 아이디는 길이가 1 이상 30 이하인 문자열이다.
- visitors는 길이가 0 이상 10,000 이하인 리스트/배열이다.
- 사용자 아이디는 알파벳 소문자로만 이루어져 있다.
위의 사항을 해당 형식으로 예외처리를 진행해준다.
그럼 이제 로직 구현 부분을 먼저 설명하고 그에 따른 구현을 보도록하자.
로직은 맨처음 입출력을 보고 생각했다.
1. 우선적으로 user에 주어진 이름을 기준으로 user의 친구들을 산출해 리스트에 따로 보관한다.
public static void setUser_F_list(String user,List<List<String>> f_list){
for (List<String> friend:f_list){
String own = friend.get(0);
String fri = friend.get(1);
if (own.equals(user)){
user_f_list.add(fri);
}else if (fri.equals(user)){
user_f_list.add(own);
}
}
}
해당 코드가 이 코드이다. 최초주어지는 인자값이 friends 이중리스트를 반복문을 돌면서 own(오너)가 user일때 fri(친구)를 리스트에 저장하고 반대로 fri가 user일때 own을 리스트에 넣는다.
2. user의 친구와 친구상태인 사람들을 hashmap에 넣어준다.
public static void getRelationFriendScore(String own,String fri,String user){
for (int i=0;i<user_f_list.size();i++){
if (own.equals(user_f_list.get(i))) {
if (fri.equals(user)) continue;
f_map.put(fri,f_map.getOrDefault(fri,0)+10);
}
else if (fri.equals(user_f_list.get(i))) {
if (own.equals(user)) continue;
f_map.put(own,f_map.getOrDefault(own,0)+10);
}
}
}
3.방문자의 점수를 넣어준다 단 이미 친구인 사람은 넣지않는다.
public static void getVisitorScore(List<String> visitors){
for (String visitor:visitors){
if(user_f_list.contains(visitor)) f_map.put(visitor,-100000);
f_map.put(visitor,f_map.getOrDefault(visitor,0)+1);
}
}
4. 최상위 5명 선발
public static void getAnswer(List<Map.Entry<String, Integer>> entryList){
if (entryList.size()>=5){
for (int i=0;i<5;i++){
if (entryList.get(i).getValue()>0){
String r_friends = entryList.get(i).getKey();
answer.add(r_friends);
}
}
}else {
for (int i=0;i<entryList.size();i++){
if (entryList.get(i).getValue()>0){
String r_friends = entryList.get(i).getKey();
answer.add(r_friends);
}
}
}
}
전체코드
package onboarding;
import java.util.*;
public class Problem7 {
static HashMap<String,Integer> f_map=new HashMap<>();
static ArrayList<String> user_f_list=new ArrayList<>();
static List<String> answer = new ArrayList<>();
public static List<String> solution(String user, List<List<String>> friends, List<String> visitors) {
if (validation("user_length",user.length())
&&
validation("friends_size",friends.size())){
setUser_F_list(user,friends);
for (List<String> friend:friends){
if(validation("friends_e_length",friend.size())){
String own = friend.get(0);
String fri = friend.get(1);
if (validation("user_valid",own)
&&
validation("user_valid",fri)){
getRelationFriendScore(own,fri,user);
}
}
}
if (validation("visitors_size",visitors.size())){
getVisitorScore(visitors);
}
List<Map.Entry<String, Integer>> entryList=new LinkedList<>(f_map.entrySet());
entryList.sort(new Comparator<Map.Entry<String, Integer>>() {
@Override
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
if (o1.getValue()==o2.getValue()){
for (int i=0;i<o1.getKey().length();i++){
for (int j=0;j<o2.getKey().length();j++){
if (o1.getKey().charAt(j)>o2.getKey().charAt(j)) return 1;
else return -1;
}
}
}
return o2.getValue()-o1.getValue();
}
});
if (!entryList.isEmpty()){
getAnswer(entryList);
}
}
return answer;
}
public static void getRelationFriendScore(String own,String fri,String user){
for (int i=0;i<user_f_list.size();i++){
if (own.equals(user_f_list.get(i))) {
if (fri.equals(user)) continue;
f_map.put(fri,f_map.getOrDefault(fri,0)+10);
}
else if (fri.equals(user_f_list.get(i))) {
if (own.equals(user)) continue;
f_map.put(own,f_map.getOrDefault(own,0)+10);
}
}
}
public static void getVisitorScore(List<String> visitors){
for (String visitor:visitors){
if(user_f_list.contains(visitor)) f_map.put(visitor,-100000);
f_map.put(visitor,f_map.getOrDefault(visitor,0)+1);
}
}
public static void getAnswer(List<Map.Entry<String, Integer>> entryList){
if (entryList.size()>=5){
for (int i=0;i<5;i++){
if (entryList.get(i).getValue()>0){
String r_friends = entryList.get(i).getKey();
answer.add(r_friends);
}
}
}else {
for (int i=0;i<entryList.size();i++){
if (entryList.get(i).getValue()>0){
String r_friends = entryList.get(i).getKey();
answer.add(r_friends);
}
}
}
}
public static void setUser_F_list(String user,List<List<String>> f_list){
for (List<String> friend:f_list){
String own = friend.get(0);
String fri = friend.get(1);
if (own.equals(user)){
user_f_list.add(fri);
}else if (fri.equals(user)){
user_f_list.add(own);
}
}
}
private static <T>boolean validation(String err_type,T err_param){
if (err_type.equals("user_length")&&err_param instanceof Integer){
int user_length = (int) err_param;
return 0< user_length&&user_length<31;
}
else if (err_type.equals("friends_size")&&err_param instanceof Integer){
int friends_size = (int) err_param;
return 0<friends_size&&friends_size<10001;
}
else if (err_type.equals("visitors_size")&&err_param instanceof Integer){
int visitors_size = (int) err_param;
return 0<visitors_size&&visitors_size<10001;
}
else if(err_type.equals("friends_e_length")&&err_param instanceof Integer){
int elements_length = (int) err_param;
return elements_length==2;
}
else if (err_type.equals("user_valid")&&err_param instanceof String){
String user_id_regex = (String) err_param;
if (!user_id_regex.matches("^[a-z]*$")) return false;
return 0<user_id_regex.length()&&user_id_regex.length()<31;
}
return false;
}
}
'대외활동' 카테고리의 다른 글
[우아한테크코스] 프리코스 4주차 리뷰 (0) | 2022.11.24 |
---|---|
[우아한테크코스] 프리코스 3주차 리뷰 (0) | 2022.11.16 |
[우아한테크코스] 프리코스 2주차 리뷰 (1) | 2022.11.09 |
졸업작품[DAMA] : 스마트쇼핑카트시연영상 (1) | 2022.09.29 |
유니톤 주최 제8회 - 해커톤 대회참가 후기(Something-New 팀) (0) | 2022.03.14 |