Making use of the serial data received via UART - a few concerns
I am specifying the length of the input that I expect from the user to my driver function, but what if the user types in more than the length? I started to see weird issues.


The first solution I ever used was if too many characters were entered consecutively without a termination character, I would clear the entire buffer and send a message back telling the user they were entering in garbage.


  So RXNE is set upon the reception of each byte, but how do I deal with something like: "temperature status" than just a single char?


You can use interrupts per character to copy it into memory to buffer it. Or better yet, use a DMA to do the copying job from the UART receive register into memory for you.

Then you can parse it all at once in the main program loop. For example, an interrupt triggers when a termination character is received and the ISR sets a flag which is is read by main loop to start a parse. Don't parse directly in the interrupt.


  Each character is stored in a specific index in a buffer. How does one parse efficiently? 


Look at the tokenizer functions in the C library. Or you can write your own...but that's really big task.


  Is circular buffer the solution where the tail would point to the index that will be the starting point of the latest input and head points to the first element? So for each input, I increment tail and head accordingly so both ends include the string that was inputted and can then parse the data? But from what I've seen, circular buffer works by moving the head when the element gets 
  removed otherwise you continue incrementing tail (with some exceptions).


Before I had a circular buffer, I had a ping-pong buffer and for this every message was already self-contained in a linear buffer so when my code was this way I used pointers to directly manipulate and parse characters in the buffer. 

Actually, that's a lie. At first I copied complete messages from the ping-pong buffer to a working buffer for parsing. But after I got things working I realized how wasteful it was to duplicate an entire message for no reason and modified things to use a pointer directly to the ping-pong buffer.

Afterwards I turned my ping pong buffer into a single intermediary linear buffer where messages were stacked inside it end to end. I added "read here next" pointers, "write here next pointers". I also had pointers for "this was the where the write pointer was when you started this read operation". There were also other routines that ensured that the "write here next pointer" was never allowed to step on the "read here next pointer". I also had routines that would determine whether the buffer was empty or full by comparing the positions and order of the "read here next" and "write here next" pointers. I ignored messages at the end of the buffer that would get cut off because this was an incremental step to a circular buffer.

The circular buffer was LOT trickier due to the wraparound and I wouldn't have been able to do it if I did not do the ping-pong buffer first. That is to say, 
it was tricky enough that I wasted a lot of time trying to apply the C-library tokenizer functions to characters that were not located sequentially in memory. I never did figure that out without writing my own tokenizer functions. 

I really wanted to keep them in the buffer for parsing using pointers like I did before but ultimately I ended up having to copy the message being parsed (book-ended by termination characters) from the circular buffer  into a linear working buffer where I could apply the C-library tokenizer functions.

Other than this, the main things added to the sequential buffer to turn it into a circular buffer was making the read and write indexes wraparound.